Skip to content

Commit 05cc859

Browse files
committed
refactor: improve data types
1 parent d104b7e commit 05cc859

12 files changed

Lines changed: 136 additions & 89 deletions

File tree

src/components/chart/Chart.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function createMark(type: string, data: Record<string, unknown>[], stacked: bool
3838
export function Chart({ args = {}, data }: ComponentProps) {
3939
const ref = useRef<HTMLDivElement>(null)
4040
const { variables, fileLoader } = useDashboard()
41-
const formatted = format_value({ ...args }, variables) as Record<string, unknown>
41+
const formatted = format_value({ ...args }, variables)
4242

4343
for (const v of validators) v(formatted)
4444

@@ -47,26 +47,38 @@ export function Chart({ args = {}, data }: ComponentProps) {
4747
data: loaded,
4848
loading,
4949
error,
50-
} = useExternalData(formattedData, formatted.loader as string | undefined, fileLoader, !!formatted.is_file)
51-
const { data: queried, loading: qLoading, error: qError } = useQuery(loaded, formatted.query as string | undefined)
50+
} = useExternalData(
51+
formattedData,
52+
typeof formatted.loader === 'string' ? formatted.loader : undefined,
53+
fileLoader,
54+
!!formatted.is_file,
55+
)
56+
const {
57+
data: queried,
58+
loading: qLoading,
59+
error: qError,
60+
} = useQuery(loaded, typeof formatted.query === 'string' ? formatted.query : undefined)
5261

5362
useEffect(() => {
5463
if (!ref.current || queried == null) return
5564
ref.current.innerHTML = ''
56-
const plotData = queried as Record<string, unknown>[]
57-
const rotated = !!(formatted.axis && (formatted.axis as Record<string, unknown>).rotated)
58-
const mark = createMark(formatted.type as string, plotData, !!formatted.stacked, rotated)
65+
const plotData = (Array.isArray(queried) ? queried : []) as Record<string, unknown>[]
66+
const axisConfig =
67+
typeof formatted.axis === 'object' && formatted.axis !== null ? (formatted.axis as Record<string, unknown>) : null
68+
const rotated = !!axisConfig?.rotated
69+
const chartType = typeof formatted.type === 'string' ? formatted.type : 'bar'
70+
const mark = createMark(chartType, plotData, !!formatted.stacked, rotated)
5971
const plotOptions: Record<string, unknown> = { marks: [mark] }
60-
if (formatted.axis) {
61-
const axisConfig = formatted.axis as Record<string, unknown>
72+
if (axisConfig) {
6273
if (axisConfig.x) plotOptions.x = axisConfig.x
6374
if (axisConfig.y) plotOptions.y = axisConfig.y
6475
}
6576
ref.current.appendChild(Plot.plot(plotOptions))
6677
}, [queried, formatted.type, formatted.stacked, formatted.axis])
6778

6879
if (loading || qLoading) return <Spinner />
69-
if (error || qError) return <ErrorMessage message={(error || qError)!} />
80+
if (error) return <ErrorMessage message={error} />
81+
if (qError) return <ErrorMessage message={qError} />
7082

7183
return <div ref={ref} className="ds--chart" />
7284
}

src/components/columns/Columns.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { DashboardRenderer } from '../DashboardRenderer'
55

66
export function Columns({ args = {}, data }: ComponentProps) {
77
const { variables } = useDashboard()
8-
const formatted = format_value({ ...args }, variables) as Record<string, unknown>
9-
const columnCount = (formatted.columns as number) ?? 2
8+
const formatted = format_value({ ...args }, variables)
9+
const columnCount = typeof formatted.columns === 'number' ? formatted.columns : 2
1010
const children = (data ?? []) as ComponentDef[]
1111

1212
return (

src/components/dropdown/Dropdown.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,29 @@ const validators = [
2121

2222
export function Dropdown({ args = {}, data }: ComponentProps) {
2323
const { variables, fileLoader, setVariable, initVariable } = useDashboard()
24-
const formatted = format_value({ ...args }, variables) as Record<string, unknown>
24+
const formatted = format_value({ ...args }, variables)
2525

2626
for (const v of validators) v(formatted)
2727

28-
const variableName = formatted.variable as string
29-
const defaultValue = formatted.default as string
28+
const variableName = typeof formatted.variable === 'string' ? formatted.variable : ''
29+
const defaultValue = typeof formatted.default === 'string' ? formatted.default : ''
3030

3131
const formattedData = format_value(data, variables)
3232
const {
3333
data: loaded,
3434
loading,
3535
error,
36-
} = useExternalData(formattedData, formatted.loader as string | undefined, fileLoader, !!formatted.is_file)
37-
const { data: queried, loading: qLoading, error: qError } = useQuery(loaded, formatted.query as string | undefined)
36+
} = useExternalData(
37+
formattedData,
38+
typeof formatted.loader === 'string' ? formatted.loader : undefined,
39+
fileLoader,
40+
!!formatted.is_file,
41+
)
42+
const {
43+
data: queried,
44+
loading: qLoading,
45+
error: qError,
46+
} = useQuery(loaded, typeof formatted.query === 'string' ? formatted.query : undefined)
3847

3948
const items = (queried as DropdownItem[] | null) ?? []
4049
const currentValue = variables[variableName]
@@ -53,12 +62,13 @@ export function Dropdown({ args = {}, data }: ComponentProps) {
5362
}, [currentValue, items, variableName, setVariable])
5463

5564
if (loading || qLoading) return <Spinner />
56-
if (error || qError) return <ErrorMessage message={(error || qError)!} />
65+
if (error) return <ErrorMessage message={error} />
66+
if (qError) return <ErrorMessage message={qError} />
5767

5868
return (
5969
<select
6070
className="ds--select"
61-
value={(currentValue as string) ?? ''}
71+
value={typeof currentValue === 'string' ? currentValue : ''}
6272
onChange={(e) => setVariable(variableName, e.target.value)}
6373
>
6474
{items.map((item) => (

src/components/root/Root.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ const validators = [required('title')]
99

1010
export function Root({ args = {}, data }: ComponentProps) {
1111
const { variables } = useDashboard()
12-
const formatted = format_value({ ...args }, variables) as Record<string, unknown>
12+
const formatted = format_value({ ...args }, variables)
1313

1414
for (const v of validators) v(formatted)
1515

16-
const title = formatted.title as string
16+
const title = typeof formatted.title === 'string' ? formatted.title : ''
1717
const children = (data ?? []) as ComponentDef[]
1818

1919
useEffect(() => {

src/components/text/Text.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const validators = [required('tagName'), regexp('tagName', /^[A-Za-z]([A-Za-z0-9
1111

1212
export function Text({ args = {}, data }: ComponentProps) {
1313
const { variables, fileLoader } = useDashboard()
14-
const formatted = format_value({ ...args }, variables) as Record<string, unknown>
14+
const formatted = format_value({ ...args }, variables)
1515

1616
for (const v of validators) v(formatted)
1717

@@ -20,18 +20,28 @@ export function Text({ args = {}, data }: ComponentProps) {
2020
data: loaded,
2121
loading,
2222
error,
23-
} = useExternalData(formattedData, formatted.loader as string | undefined, fileLoader, !!formatted.is_file)
24-
const { data: queried, loading: qLoading, error: qError } = useQuery(loaded, formatted.query as string | undefined)
23+
} = useExternalData(
24+
formattedData,
25+
typeof formatted.loader === 'string' ? formatted.loader : undefined,
26+
fileLoader,
27+
!!formatted.is_file,
28+
)
29+
const {
30+
data: queried,
31+
loading: qLoading,
32+
error: qError,
33+
} = useQuery(loaded, typeof formatted.query === 'string' ? formatted.query : undefined)
2534

2635
if (loading || qLoading) return <Spinner />
27-
if (error || qError) return <ErrorMessage message={(error || qError)!} />
36+
if (error) return <ErrorMessage message={error} />
37+
if (qError) return <ErrorMessage message={qError} />
2838

29-
const Tag = formatted.tagName as keyof React.JSX.IntrinsicElements
30-
const alignProps = formatted.align ? { 'data-align': formatted.align as string } : {}
39+
const Tag = (typeof formatted.tagName === 'string' ? formatted.tagName : 'div') as keyof React.JSX.IntrinsicElements
40+
const alignProps = typeof formatted.align === 'string' ? { 'data-align': formatted.align } : {}
3141

3242
return (
3343
<Tag className="ds--text" {...alignProps}>
34-
{queried as string}
44+
{String(queried)}
3545
</Tag>
3646
)
3747
}

src/hooks/useExternalData.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import type { FileLoader } from '../types'
33

44
type LoaderName = 'csv' | 'json' | 'tsv' | 'text'
55

6+
const LOADER_NAMES = new Set<string>(['csv', 'json', 'tsv', 'text'])
7+
8+
function isLoaderName(value: string): value is LoaderName {
9+
return LOADER_NAMES.has(value)
10+
}
11+
612
function parseCsv(text: string): Record<string, string>[] {
713
const lines = text.trim().split('\n')
814
if (lines.length === 0) return []
@@ -61,7 +67,7 @@ export function useExternalData(
6167
return
6268
}
6369

64-
const source = data as string
70+
const source = typeof data === 'string' ? data : String(data)
6571
setLoading(true)
6672
setError(null)
6773

@@ -73,7 +79,7 @@ export function useExternalData(
7379
return
7480
}
7581
try {
76-
setLoadedData(parseData(rawText, loader as LoaderName))
82+
setLoadedData(parseData(rawText, isLoaderName(loader) ? loader : 'text'))
7783
} catch (e) {
7884
setError(String(e))
7985
}
@@ -83,7 +89,7 @@ export function useExternalData(
8389
fetch(source)
8490
.then((res) => res.text())
8591
.then((text) => {
86-
setLoadedData(parseData(text, loader as LoaderName))
92+
setLoadedData(parseData(text, isLoaderName(loader) ? loader : 'text'))
8793
setLoading(false)
8894
})
8995
.catch((e) => {

src/interpolation.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ tests.forEach(([test_suite_name, function_name, test_sets]) =>
6161
;(test_sets as unknown[][][]).forEach((test_set: unknown[][]) =>
6262
test_set.forEach(([input, output, state]) =>
6363
it(`${[stringify(input), stringify(output), stringify(state)]}`, () => {
64-
expect((module as Record<string, any>)[function_name as string](input, state)).toEqual(output)
64+
expect(
65+
(module as Record<string, (input: unknown, state: unknown) => unknown>)[function_name as string](
66+
input,
67+
state,
68+
),
69+
).toEqual(output)
6570
}),
6671
),
6772
)

src/interpolation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ const format_array = (input: unknown[], state: State): unknown[] => {
1212
return input.map((item) => format_value(item, state))
1313
}
1414

15-
const format_value = (input: unknown, state: State): unknown => {
15+
function format_value(input: Record<string, unknown>, state: State): Record<string, unknown>
16+
function format_value(input: unknown, state: State): unknown
17+
function format_value(input: unknown, state: State): unknown {
1618
if (input === null) return null
1719
if (Array.isArray(input)) return format_array(input, state)
1820
if (typeof input === 'string') return format_string(input, state)

src/jq-web.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const emuto = async (data: unknown, query: string): Promise<unknown> => {
22
const emuto_ = await import('emuto/lib/interpreter')
3-
console.log(emuto_) // eslint-disable-line
43
return emuto_.default(query)(data)
54
}
65

src/validators.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ export const required =
77
export const regexp =
88
(arg_name: string, expression: RegExp) =>
99
(args: Record<string, unknown>): void => {
10-
if (!(args[arg_name] as string).match(expression)) throw new Error(`Argument '${arg_name}' is invalid`)
10+
const value = args[arg_name]
11+
if (typeof value !== 'string' || !value.match(expression)) throw new Error(`Argument '${arg_name}' is invalid`)
1112
}

0 commit comments

Comments
 (0)