@dxos/react-ui-table 0.6.12-main.15a606f → 0.6.12-main.568932b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +48 -59
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts +12 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts.map +1 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts +83 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -0
- package/dist/types/src/components/ColumnMenu/index.d.ts +1 -1
- package/dist/types/src/components/ColumnMenu/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +42 -0
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableContext.d.ts +2 -4
- package/dist/types/src/components/Table/TableContext.d.ts.map +1 -1
- package/dist/types/src/components/Table/index.d.ts +2 -1
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- package/dist/types/src/helpers.d.ts +1 -1
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/schema/{SchemaToTable.stories.d.ts → Schema.stories.d.ts} +2 -3
- package/dist/types/src/schema/Schema.stories.d.ts.map +1 -0
- package/dist/types/src/schema/{schemaToColumns.d.ts → column-utils.d.ts} +2 -2
- package/dist/types/src/schema/column-utils.d.ts.map +1 -0
- package/dist/types/src/schema/index.d.ts +3 -1
- package/dist/types/src/schema/index.d.ts.map +1 -1
- package/dist/types/src/schema/schema.d.ts +2 -3
- package/dist/types/src/schema/schema.d.ts.map +1 -1
- package/dist/types/src/schema/types.d.ts +1 -1
- package/dist/types/src/schema/types.d.ts.map +1 -1
- package/package.json +19 -17
- package/src/components/ColumnMenu/ColumnMenu.stories.tsx +9 -9
- package/src/components/ColumnMenu/ColumnMenu.tsx +2 -2
- package/src/components/ColumnMenu/{ColumnSettingsForm.stories.tsx → ColumnSettings.stories.tsx} +18 -15
- package/src/components/ColumnMenu/{ColumnSettingsForm.tsx → ColumnSettings.tsx} +33 -36
- package/src/components/ColumnMenu/index.ts +1 -1
- package/src/components/Form/Form.stories.tsx +69 -0
- package/src/components/Table/Table.stories.tsx +2 -2
- package/src/components/Table/Table.tsx +2 -2
- package/src/components/Table/TableContext.tsx +13 -7
- package/src/components/Table/index.ts +2 -1
- package/src/helpers.tsx +4 -6
- package/src/schema/{SchemaToTable.stories.tsx → Schema.stories.tsx} +6 -15
- package/src/schema/{schemaToColumns.ts → column-utils.ts} +5 -5
- package/src/schema/index.ts +3 -1
- package/src/schema/schema.test.ts +20 -91
- package/src/schema/schema.ts +23 -30
- package/src/schema/types.ts +4 -2
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts +0 -12
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +0 -1
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts +0 -63
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts.map +0 -1
- package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +0 -1
- package/dist/types/src/schema/schemaToColumns.d.ts.map +0 -1
package/src/components/ColumnMenu/{ColumnSettingsForm.stories.tsx → ColumnSettings.stories.tsx}
RENAMED
|
@@ -3,17 +3,29 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
|
+
|
|
6
7
|
import React from 'react';
|
|
7
8
|
|
|
8
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
9
|
+
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import { ColumnSettings, type ColumnSettingsProps } from './ColumnSettings';
|
|
11
12
|
import { type TableDef } from '../../schema';
|
|
13
|
+
import translations from '../../translations';
|
|
14
|
+
|
|
15
|
+
const Story = (props: ColumnSettingsProps) => (
|
|
16
|
+
<div className='flex w-[240px] m-2 p-2 border border-separator rounded'>
|
|
17
|
+
<ColumnSettings {...props} />
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
12
20
|
|
|
13
21
|
export default {
|
|
14
|
-
title: 'react-ui-table/
|
|
15
|
-
component:
|
|
16
|
-
decorators: [withTheme],
|
|
22
|
+
title: 'react-ui-table/ColumnSettings',
|
|
23
|
+
component: ColumnSettings,
|
|
24
|
+
decorators: [withTheme, withLayout()],
|
|
25
|
+
parameters: {
|
|
26
|
+
translations,
|
|
27
|
+
},
|
|
28
|
+
render: Story,
|
|
17
29
|
};
|
|
18
30
|
|
|
19
31
|
const sampleTableDef: TableDef = {
|
|
@@ -37,7 +49,7 @@ const sampleTablesToReference: TableDef[] = [
|
|
|
37
49
|
},
|
|
38
50
|
];
|
|
39
51
|
|
|
40
|
-
const defaultArgs:
|
|
52
|
+
const defaultArgs: ColumnSettingsProps = {
|
|
41
53
|
column: {
|
|
42
54
|
id: 'col-1',
|
|
43
55
|
prop: 'name',
|
|
@@ -93,12 +105,3 @@ export const NewColumn = {
|
|
|
93
105
|
},
|
|
94
106
|
},
|
|
95
107
|
};
|
|
96
|
-
|
|
97
|
-
const Template = (args: ColumnSettingsFormProps) => (
|
|
98
|
-
<div className='p-4 max-is-64'>
|
|
99
|
-
<ColumnSettingsForm {...args} />
|
|
100
|
-
</div>
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
export const Interactive = Template.bind({});
|
|
104
|
-
(Interactive as any).args = defaultArgs;
|
|
@@ -10,23 +10,24 @@ import { safeParseInt } from '@dxos/util';
|
|
|
10
10
|
import { type TableDef, type ColumnProps, type ColumnType } from '../../schema';
|
|
11
11
|
import { translationKey } from '../../translations';
|
|
12
12
|
|
|
13
|
-
export type
|
|
13
|
+
export type ColumnSettingsProps = {
|
|
14
14
|
column: ColumnProps;
|
|
15
15
|
tableDef: TableDef;
|
|
16
|
+
// TODO(burdon): Rename.
|
|
16
17
|
tablesToReference: TableDef[];
|
|
17
|
-
onUpdate?: (
|
|
18
|
-
onDelete?: (
|
|
18
|
+
onUpdate?: (id: string, column: ColumnProps) => void;
|
|
19
|
+
onDelete?: (id: string) => void;
|
|
19
20
|
onClose?: () => void;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export const
|
|
23
|
+
export const ColumnSettings = ({
|
|
23
24
|
column,
|
|
24
25
|
tableDef,
|
|
25
26
|
tablesToReference,
|
|
26
27
|
onUpdate,
|
|
27
28
|
onDelete,
|
|
28
29
|
onClose,
|
|
29
|
-
}:
|
|
30
|
+
}: ColumnSettingsProps) => {
|
|
30
31
|
const [formState, setFormState] = useState({
|
|
31
32
|
prop: column.id,
|
|
32
33
|
refTable: column.refTable,
|
|
@@ -76,9 +77,10 @@ export const ColumnSettingsForm = ({
|
|
|
76
77
|
onClose?.();
|
|
77
78
|
};
|
|
78
79
|
|
|
80
|
+
// TODO(burdon): Standardize dialog/popup.
|
|
79
81
|
return (
|
|
80
|
-
<div className='
|
|
81
|
-
<div className='
|
|
82
|
+
<div className='flex flex-col w-full gap-4'>
|
|
83
|
+
<div className='flex flex-col gap-2'>
|
|
82
84
|
<Input.Root>
|
|
83
85
|
<Input.Label>{t('column label label')}</Input.Label>
|
|
84
86
|
<Input.TextInput
|
|
@@ -89,8 +91,7 @@ export const ColumnSettingsForm = ({
|
|
|
89
91
|
data-testid='table.column-settings.label'
|
|
90
92
|
/>
|
|
91
93
|
</Input.Root>
|
|
92
|
-
|
|
93
|
-
<div className='space-y-1'>
|
|
94
|
+
|
|
94
95
|
<Input.Root>
|
|
95
96
|
<Input.Label>{t('property key label')}</Input.Label>
|
|
96
97
|
<Input.TextInput
|
|
@@ -102,8 +103,7 @@ export const ColumnSettingsForm = ({
|
|
|
102
103
|
}
|
|
103
104
|
/>
|
|
104
105
|
</Input.Root>
|
|
105
|
-
|
|
106
|
-
<div className='space-y-1'>
|
|
106
|
+
|
|
107
107
|
<Input.Root>
|
|
108
108
|
<Input.Label>{t('column type label')}</Input.Label>
|
|
109
109
|
<Select.Root
|
|
@@ -124,9 +124,8 @@ export const ColumnSettingsForm = ({
|
|
|
124
124
|
</Select.Portal>
|
|
125
125
|
</Select.Root>
|
|
126
126
|
</Input.Root>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<div className='space-y-1'>
|
|
127
|
+
|
|
128
|
+
{formState.type === 'number' && (
|
|
130
129
|
<Input.Root>
|
|
131
130
|
<Input.Label>{t('digits label')}</Input.Label>
|
|
132
131
|
<Input.TextInput
|
|
@@ -134,12 +133,10 @@ export const ColumnSettingsForm = ({
|
|
|
134
133
|
onChange={(event) => setFormState((prevState) => ({ ...prevState, digits: event.target.value }))}
|
|
135
134
|
/>
|
|
136
135
|
</Input.Root>
|
|
137
|
-
|
|
138
|
-
)}
|
|
136
|
+
)}
|
|
139
137
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<div className='space-y-1'>
|
|
138
|
+
{formState.type === 'ref' && (
|
|
139
|
+
<>
|
|
143
140
|
<Input.Root>
|
|
144
141
|
<Input.Label>Table</Input.Label>
|
|
145
142
|
<Select.Root
|
|
@@ -162,9 +159,8 @@ export const ColumnSettingsForm = ({
|
|
|
162
159
|
</Select.Portal>
|
|
163
160
|
</Select.Root>
|
|
164
161
|
</Input.Root>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
<div className='space-y-1'>
|
|
162
|
+
|
|
163
|
+
{formState.refTable && (
|
|
168
164
|
<Input.Root>
|
|
169
165
|
<Input.Label>Table property</Input.Label>
|
|
170
166
|
<Select.Root
|
|
@@ -187,20 +183,21 @@ export const ColumnSettingsForm = ({
|
|
|
187
183
|
</Select.Portal>
|
|
188
184
|
</Select.Root>
|
|
189
185
|
</Input.Root>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<div
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
186
|
+
)}
|
|
187
|
+
</>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div>
|
|
192
|
+
{/* TODO(burdon): Change delete to cancel. Delete should be menu item. */}
|
|
193
|
+
<div className='flex flex-row justify-end gap-1'>
|
|
194
|
+
<Button onClick={handleDelete} data-testid='table.column-settings.delete'>
|
|
195
|
+
{t('delete label')}
|
|
196
|
+
</Button>
|
|
197
|
+
<Button variant='primary' onClick={handleSave} data-testid='table.column-settings.save'>
|
|
198
|
+
{t('save label')}
|
|
199
|
+
</Button>
|
|
200
|
+
</div>
|
|
204
201
|
</div>
|
|
205
202
|
</div>
|
|
206
203
|
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { AST, S } from '@dxos/echo-schema';
|
|
10
|
+
import { Input } from '@dxos/react-ui';
|
|
11
|
+
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import translations from '../../translations';
|
|
14
|
+
|
|
15
|
+
const Story = (props: FormProps) => (
|
|
16
|
+
<div className='flex w-[240px] m-2 p-2 border border-separator rounded'>
|
|
17
|
+
<Form {...props} />
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
type FormProps = {
|
|
22
|
+
schema: S.Schema<any>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const Form = ({ schema }: FormProps) => {
|
|
26
|
+
// const types = getColumnTypes(schema);
|
|
27
|
+
const props = AST.getPropertySignatures(schema.ast);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className='flex flex-col w-full gap-2'>
|
|
31
|
+
{props.map((prop, i) => {
|
|
32
|
+
const { name, annotations } = prop;
|
|
33
|
+
const a = AST.getDescriptionAnnotation(prop);
|
|
34
|
+
console.log(a, annotations);
|
|
35
|
+
return (
|
|
36
|
+
<Input.Root key={i}>
|
|
37
|
+
<Input.Label>{name.toString()}</Input.Label>
|
|
38
|
+
<Input.TextInput
|
|
39
|
+
placeholder={name.toString()}
|
|
40
|
+
// value={formState.label ?? ''}
|
|
41
|
+
// onChange={(event) => setFormState((prevState) => ({ ...prevState, label: event.target.value }))}
|
|
42
|
+
/>
|
|
43
|
+
</Input.Root>
|
|
44
|
+
);
|
|
45
|
+
})}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const schema = S.Struct({
|
|
51
|
+
name: S.String.annotations({ [AST.DescriptionAnnotationId]: 'Name' }),
|
|
52
|
+
email: S.String,
|
|
53
|
+
age: S.Number,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export default {
|
|
57
|
+
title: 'react-ui-table/Form',
|
|
58
|
+
decorators: [withTheme, withLayout()],
|
|
59
|
+
parameters: {
|
|
60
|
+
translations,
|
|
61
|
+
},
|
|
62
|
+
render: Story,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const Default = {
|
|
66
|
+
args: {
|
|
67
|
+
schema,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -8,7 +8,7 @@ import { Plugs, PlugsConnected } from '@phosphor-icons/react';
|
|
|
8
8
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { create } from '@dxos/echo-schema';
|
|
11
|
-
import {
|
|
11
|
+
import { registerSignalsRuntime } from '@dxos/echo-signals/react';
|
|
12
12
|
import { PublicKey } from '@dxos/keys';
|
|
13
13
|
import { faker } from '@dxos/random';
|
|
14
14
|
import { Button, DensityProvider } from '@dxos/react-ui';
|
|
@@ -406,7 +406,7 @@ export const TenThousandRows = {
|
|
|
406
406
|
},
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
-
|
|
409
|
+
registerSignalsRuntime();
|
|
410
410
|
const state = create({ items: createItems(10) });
|
|
411
411
|
|
|
412
412
|
export const RealTimeUpdates = {
|
|
@@ -79,8 +79,8 @@ export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>)
|
|
|
79
79
|
pinLastRow,
|
|
80
80
|
} = props;
|
|
81
81
|
|
|
82
|
-
const columns = useDefaultValue(props.columns, []);
|
|
83
|
-
const incomingData = useDefaultValue(props.data, []);
|
|
82
|
+
const columns = useDefaultValue(props.columns, () => []);
|
|
83
|
+
const incomingData = useDefaultValue(props.data, () => []);
|
|
84
84
|
|
|
85
85
|
const [data, setData] = useState([...incomingData]);
|
|
86
86
|
|
|
@@ -3,26 +3,32 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type RowData } from '@tanstack/react-table';
|
|
6
|
-
import React, {
|
|
6
|
+
import React, {
|
|
7
|
+
useContext,
|
|
8
|
+
createContext,
|
|
9
|
+
type Context,
|
|
10
|
+
type JSX,
|
|
11
|
+
type PropsWithChildren,
|
|
12
|
+
type ReactNode,
|
|
13
|
+
} from 'react';
|
|
14
|
+
|
|
15
|
+
import { raise } from '@dxos/debug';
|
|
7
16
|
|
|
8
17
|
import { type TableContextValue } from './props';
|
|
9
18
|
|
|
10
19
|
export const TABLE_NAME = 'Table';
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
// The expectation is that the provider will always supply a value, so no need to define a default here.
|
|
14
|
-
const TableContext = createContext<TableContextValue<RowData>>({} as TableContextValue<RowData>);
|
|
21
|
+
const TableContext = createContext<TableContextValue<RowData> | undefined>(undefined);
|
|
15
22
|
|
|
16
23
|
export type TypedTableProvider<TData extends RowData> = {
|
|
17
24
|
(props: TableContextValue<TData> & { children: ReactNode }): JSX.Element;
|
|
18
25
|
displayName: string;
|
|
19
26
|
};
|
|
20
27
|
|
|
21
|
-
export const TableProvider
|
|
28
|
+
export const TableProvider = ({ children, ...value }: PropsWithChildren<TableContextValue<RowData>>) => {
|
|
22
29
|
return <TableContext.Provider value={value}>{children}</TableContext.Provider>;
|
|
23
30
|
};
|
|
24
31
|
|
|
25
|
-
// Create a custom hook to consume the context.
|
|
26
32
|
export const useTableContext = <TData extends RowData>(): TableContextValue<TData> => {
|
|
27
|
-
return useContext(TableContext as Context<TableContextValue<TData>>);
|
|
33
|
+
return useContext(TableContext as Context<TableContextValue<TData>>) ?? raise(new Error('Missing TableContext'));
|
|
28
34
|
};
|
package/src/helpers.tsx
CHANGED
|
@@ -21,8 +21,7 @@ import { log } from '@dxos/log';
|
|
|
21
21
|
import { type ClassNameValue, Input, Tooltip } from '@dxos/react-ui';
|
|
22
22
|
import { getSize, mx } from '@dxos/react-ui-theme';
|
|
23
23
|
|
|
24
|
-
import { CellCombobox } from './components';
|
|
25
|
-
import { useTableContext } from './components/Table/TableContext';
|
|
24
|
+
import { CellCombobox, useTableContext } from './components';
|
|
26
25
|
import { textPadding } from './theme';
|
|
27
26
|
|
|
28
27
|
export type ValueUpdater<TData extends RowData, TValue> = (row: TData, id: string, value: TValue) => void;
|
|
@@ -470,7 +469,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
470
469
|
size: 50,
|
|
471
470
|
minSize: 50,
|
|
472
471
|
header: (column) => label ?? column.header.id,
|
|
473
|
-
// TODO(zan): Implement sort algorithm
|
|
472
|
+
// TODO(zan): Implement sort algorithm.
|
|
474
473
|
enableSorting: true,
|
|
475
474
|
cell: SwitchBuilderCell,
|
|
476
475
|
...props,
|
|
@@ -486,7 +485,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
486
485
|
const IconOff = off?.Icon ?? X;
|
|
487
486
|
return {
|
|
488
487
|
size: size ?? 32,
|
|
489
|
-
// TODO(zan): Implement sort algorithm
|
|
488
|
+
// TODO(zan): Implement sort algorithm.
|
|
490
489
|
enableSorting: false,
|
|
491
490
|
header: (column) => <div className={'justify-center'}>{label ?? column.header.id}</div>,
|
|
492
491
|
cell: (cell) => {
|
|
@@ -506,14 +505,13 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
506
505
|
/**
|
|
507
506
|
* This display column type is for nested/unhandled data to appear as JSON.
|
|
508
507
|
*/
|
|
509
|
-
|
|
508
|
+
json({ label, classNames, ...props }: KeyColumnOptions<TData> = {}): Partial<ColumnDef<TData, PublicKey>> {
|
|
510
509
|
return {
|
|
511
510
|
size: 120,
|
|
512
511
|
minSize: 120,
|
|
513
512
|
header: (cell) => label ?? cell.header.id,
|
|
514
513
|
cell: (cell) => {
|
|
515
514
|
const value = cell.getValue();
|
|
516
|
-
|
|
517
515
|
return (
|
|
518
516
|
<div className='overflow-scroll'>
|
|
519
517
|
<code className='text-xs'>{JSON.stringify(value)}</code>
|
|
@@ -4,28 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import { Arbitrary } from '@effect/schema';
|
|
8
8
|
import * as fc from 'fast-check';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
|
|
11
11
|
import { S } from '@dxos/echo-schema';
|
|
12
|
-
import {
|
|
13
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
14
13
|
|
|
15
|
-
import { schemaToColumnDefs } from './
|
|
14
|
+
import { schemaToColumnDefs } from './column-utils';
|
|
16
15
|
import { Table } from '../components';
|
|
17
16
|
|
|
18
17
|
export default {
|
|
19
|
-
title: 'react-ui-table/
|
|
20
|
-
|
|
21
|
-
decorators: [
|
|
22
|
-
withTheme,
|
|
23
|
-
(Story: any) => (
|
|
24
|
-
<DensityProvider density='fine'>
|
|
25
|
-
<Story />
|
|
26
|
-
</DensityProvider>
|
|
27
|
-
),
|
|
28
|
-
],
|
|
18
|
+
title: 'react-ui-table/Schema',
|
|
19
|
+
decorators: [withTheme, withLayout()],
|
|
29
20
|
};
|
|
30
21
|
|
|
31
22
|
const exampleSchema = S.Struct({
|
|
@@ -49,7 +40,7 @@ const exampleSchemaArbitrary = Arbitrary.make(exampleSchema);
|
|
|
49
40
|
|
|
50
41
|
const items = fc.sample(exampleSchemaArbitrary, 10);
|
|
51
42
|
|
|
52
|
-
export const
|
|
43
|
+
export const Default = {
|
|
53
44
|
render: () => {
|
|
54
45
|
return (
|
|
55
46
|
<Table.Root>
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ColumnDef } from '@tanstack/react-table';
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { type S } from '@dxos/echo-schema';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { getColumnTypes } from './schema';
|
|
10
10
|
import { createColumnBuilder } from '../helpers';
|
|
11
11
|
|
|
12
12
|
export const schemaToColumnDefs = <T>(schema: S.Schema<T, any>): ColumnDef<T, any>[] => {
|
|
13
13
|
const { helper, builder } = createColumnBuilder<T>();
|
|
14
14
|
|
|
15
|
-
const classified =
|
|
15
|
+
const classified = getColumnTypes(schema);
|
|
16
16
|
return classified.map(([name, type]) => {
|
|
17
17
|
const propertyKey = name.toString();
|
|
18
18
|
|
|
@@ -34,8 +34,8 @@ export const schemaToColumnDefs = <T>(schema: S.Schema<T, any>): ColumnDef<T, an
|
|
|
34
34
|
column = builder.date({ label: propertyKey });
|
|
35
35
|
break;
|
|
36
36
|
}
|
|
37
|
-
case '
|
|
38
|
-
column = builder.
|
|
37
|
+
case 'json': {
|
|
38
|
+
column = builder.json({ label: propertyKey, id: propertyKey });
|
|
39
39
|
break;
|
|
40
40
|
}
|
|
41
41
|
default: {
|
package/src/schema/index.ts
CHANGED
|
@@ -2,63 +2,49 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Schema as S } from '@effect/schema';
|
|
5
6
|
import { describe, expect, test } from 'vitest';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { getColumnTypes } from './schema';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
// TODO(burdon): Realistic data.
|
|
10
11
|
|
|
11
|
-
describe('
|
|
12
|
+
describe('getColumnTypes', () => {
|
|
12
13
|
test('basic', () => {
|
|
13
|
-
const
|
|
14
|
+
const schema = S.Struct({
|
|
14
15
|
field1: S.String,
|
|
15
16
|
field2: S.Number,
|
|
17
|
+
field3: S.Date,
|
|
16
18
|
});
|
|
17
19
|
|
|
18
|
-
const columns =
|
|
19
|
-
|
|
20
|
+
const columns = getColumnTypes(schema);
|
|
20
21
|
expect(columns).to.deep.equal([
|
|
21
22
|
['field1', 'string'],
|
|
22
23
|
['field2', 'number'],
|
|
24
|
+
['field3', 'date'],
|
|
23
25
|
]);
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
test('optional properties', () => {
|
|
27
|
-
const
|
|
29
|
+
const schema = S.Struct({
|
|
28
30
|
field1: S.optional(S.String),
|
|
29
31
|
field2: S.optional(S.Number),
|
|
30
32
|
});
|
|
31
33
|
|
|
32
|
-
const columns =
|
|
33
|
-
|
|
34
|
+
const columns = getColumnTypes(schema);
|
|
34
35
|
expect(columns).to.deep.equal([
|
|
35
36
|
['field1', 'string'],
|
|
36
37
|
['field2', 'number'],
|
|
37
38
|
]);
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
test('
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
field2: S.optional(S.Date),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const columns = classifySchemaProperties(simpleSchema);
|
|
47
|
-
|
|
48
|
-
expect(columns).to.deep.equal([
|
|
49
|
-
['field1', 'date'],
|
|
50
|
-
['field2', 'date'],
|
|
51
|
-
]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('Piped validators', () => {
|
|
55
|
-
const simpleSchema = S.Struct({
|
|
56
|
-
name: S.optional(S.String.pipe(S.nonEmpty(), S.maxLength(10))),
|
|
41
|
+
test('piped validators', () => {
|
|
42
|
+
const schema = S.Struct({
|
|
43
|
+
name: S.optional(S.String.pipe(S.nonEmptyString(), S.maxLength(10))),
|
|
57
44
|
age: S.Number.pipe(S.negative()),
|
|
58
45
|
});
|
|
59
46
|
|
|
60
|
-
const columns =
|
|
61
|
-
|
|
47
|
+
const columns = getColumnTypes(schema);
|
|
62
48
|
expect(columns).to.deep.equal([
|
|
63
49
|
['name', 'string'],
|
|
64
50
|
['age', 'number'],
|
|
@@ -66,81 +52,24 @@ describe('schema->column-type', () => {
|
|
|
66
52
|
});
|
|
67
53
|
|
|
68
54
|
test('nested schema', () => {
|
|
69
|
-
const
|
|
70
|
-
name: S.String,
|
|
71
|
-
nested: S.Struct({
|
|
72
|
-
age: S.Number,
|
|
73
|
-
}),
|
|
74
|
-
someUnion: S.Union(S.String, S.Number),
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const columns = classifySchemaProperties(simpleSchema);
|
|
78
|
-
|
|
79
|
-
expect(columns).to.deep.equal([
|
|
80
|
-
['name', 'string'],
|
|
81
|
-
['nested.age', 'number'],
|
|
82
|
-
['someUnion', 'display'],
|
|
83
|
-
]);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('deeply nested schema', () => {
|
|
87
|
-
const simpleSchema = S.Struct({
|
|
55
|
+
const schema = S.Struct({
|
|
88
56
|
name: S.String,
|
|
89
|
-
|
|
57
|
+
nested1: S.Struct({
|
|
90
58
|
age: S.Number,
|
|
91
59
|
deeplyNested: S.Struct({
|
|
92
|
-
height: S.Number,
|
|
60
|
+
height: S.optional(S.Number),
|
|
93
61
|
}),
|
|
94
62
|
}),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const columns = classifySchemaProperties(simpleSchema);
|
|
98
|
-
|
|
99
|
-
expect(columns).to.deep.equal([
|
|
100
|
-
['name', 'string'],
|
|
101
|
-
['nested.age', 'number'],
|
|
102
|
-
['nested.deeplyNested.height', 'number'],
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('even more deeply nested schema with optionals', () => {
|
|
107
|
-
const simpleSchema = S.Struct({
|
|
108
|
-
name: S.String,
|
|
109
|
-
nested: S.Struct({
|
|
110
|
-
age: S.Number,
|
|
111
|
-
deeplyNested: S.optional(
|
|
112
|
-
S.Struct({
|
|
113
|
-
height: S.optional(S.Number),
|
|
114
|
-
}),
|
|
115
|
-
),
|
|
116
|
-
}),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const columns = classifySchemaProperties(simpleSchema);
|
|
120
|
-
|
|
121
|
-
expect(columns).to.deep.equal([
|
|
122
|
-
['name', 'string'],
|
|
123
|
-
['nested.age', 'number'],
|
|
124
|
-
['nested.deeplyNested.height', 'number'],
|
|
125
|
-
]);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('multiple-nested schemata as sibblings', () => {
|
|
129
|
-
const simpleSchema = S.Struct({
|
|
130
|
-
name: S.String,
|
|
131
|
-
nested: S.Struct({
|
|
132
|
-
age: S.Number,
|
|
133
|
-
}),
|
|
134
63
|
nested2: S.Struct({
|
|
135
64
|
height: S.Number,
|
|
136
65
|
}),
|
|
137
66
|
});
|
|
138
67
|
|
|
139
|
-
const columns =
|
|
140
|
-
|
|
68
|
+
const columns = getColumnTypes(schema);
|
|
141
69
|
expect(columns).to.deep.equal([
|
|
142
70
|
['name', 'string'],
|
|
143
|
-
['
|
|
71
|
+
['nested1.age', 'number'],
|
|
72
|
+
['nested1.deeplyNested.height', 'number'],
|
|
144
73
|
['nested2.height', 'number'],
|
|
145
74
|
]);
|
|
146
75
|
});
|