@dxos/react-ui-table 0.8.4-main.72ec0f3 → 0.8.4-main.7996785055
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/chunk-BF54SSNL.mjs +70 -0
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GS2UNGJK.mjs → chunk-EK6D2CVM.mjs} +656 -680
- package/dist/lib/browser/chunk-EK6D2CVM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1148 -990
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -42
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node-esm/{chunk-TRR3C5NX.mjs → chunk-LYYDF6JP.mjs} +656 -679
- package/dist/lib/node-esm/chunk-LYYDF6JP.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs +71 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1148 -990
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +30 -42
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +4 -18
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +13 -8
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +28 -21
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +74 -7
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +21 -0
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +25 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +7 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -15
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +9 -9
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +104 -24
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +3 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +4 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +26 -0
- package/dist/types/src/types/Table.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +14 -11
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -55
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +16 -16
- package/src/components/Table/DynamicTable.tsx +32 -31
- package/src/components/Table/Relations.stories.tsx +58 -53
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +111 -94
- package/src/components/Table/Table.tsx +43 -406
- package/src/components/Table/TableContent.tsx +422 -0
- package/src/components/{TableToolbar → Table}/TableToolbar.tsx +42 -43
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -5
- package/src/components/TableCellEditor/FormCellEditor.tsx +118 -135
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +16 -12
- package/src/components/TableCellEditor/TableCellEditor.tsx +37 -32
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +38 -12
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +82 -54
- package/src/model/table-model.ts +430 -167
- package/src/model/table-presentation.test.ts +59 -49
- package/src/model/table-presentation.ts +43 -37
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +49 -5
- package/src/testing/useTestTableModel.ts +45 -38
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +79 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +40 -26
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-2PFAPQL4.mjs +0 -66
- package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +0 -7
- package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs +0 -67
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +0 -7
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/dist/types/src/model/table-sorting.d.ts +0 -52
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -28
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/src/components/TableToolbar/index.ts +0 -5
- package/src/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
|
@@ -2,26 +2,28 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
5
6
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { expect, userEvent, within } from 'storybook/test';
|
|
7
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
8
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
9
9
|
|
|
10
10
|
import { Obj, Type } from '@dxos/echo';
|
|
11
|
-
import {
|
|
11
|
+
import { View } from '@dxos/echo';
|
|
12
|
+
import { invariant } from '@dxos/invariant';
|
|
12
13
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
13
|
-
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
14
14
|
import { faker } from '@dxos/random';
|
|
15
|
-
import {
|
|
16
|
-
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
15
|
+
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
17
16
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
18
17
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
19
18
|
import { translations as formTranslations } from '@dxos/react-ui-form';
|
|
20
|
-
import {
|
|
19
|
+
import { ViewModel } from '@dxos/schema';
|
|
21
20
|
import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
|
|
21
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
22
22
|
import { Organization, Person } from '@dxos/types';
|
|
23
23
|
|
|
24
|
-
import
|
|
24
|
+
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
25
|
+
|
|
26
|
+
import { useProjectionModel, useTableModel } from '../../hooks';
|
|
25
27
|
import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
|
|
26
28
|
import { translations } from '../../translations';
|
|
27
29
|
import { Table } from '../../types';
|
|
@@ -35,11 +37,10 @@ const generator: ValueGenerator = faker as any;
|
|
|
35
37
|
// TODO(burdon): Mutable and immutable views.
|
|
36
38
|
// TODO(burdon): Reconcile schemas types and utils (see API PR).
|
|
37
39
|
// TODO(burdon): Base type for T (with id); see ECHO API PR?
|
|
38
|
-
const useTestModel = <S extends Type.
|
|
39
|
-
const
|
|
40
|
-
const { space } =
|
|
41
|
-
const [
|
|
42
|
-
const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
|
|
40
|
+
const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
|
|
41
|
+
const registry = useContext(RegistryContext);
|
|
42
|
+
const { space } = useClientStory();
|
|
43
|
+
const [object, setObject] = useState<Table.Table>();
|
|
43
44
|
|
|
44
45
|
const features = useMemo<TableFeatures>(
|
|
45
46
|
() => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
|
|
@@ -51,18 +52,14 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
const {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
setJsonSchema(jsonSchema);
|
|
60
|
-
setView(view);
|
|
61
|
-
space.db.add(view);
|
|
62
|
-
await space.db.schemaRegistry.register([effectSchema]);
|
|
63
|
-
}, [client, space, schema]);
|
|
55
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
|
|
56
|
+
const object = Table.make({ view, jsonSchema });
|
|
57
|
+
setObject(object);
|
|
58
|
+
space.db.add(object);
|
|
59
|
+
}, [space, schema]);
|
|
64
60
|
|
|
65
|
-
const
|
|
61
|
+
const projection = useProjectionModel(schema, object, registry);
|
|
62
|
+
const model = useTableModel<TableRow>({ object, projection, db: space?.db, features });
|
|
66
63
|
|
|
67
64
|
useEffect(() => {
|
|
68
65
|
if (!model || !space) {
|
|
@@ -80,45 +77,44 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
80
77
|
return;
|
|
81
78
|
}
|
|
82
79
|
|
|
83
|
-
return new TablePresentation(model);
|
|
84
|
-
}, [model]);
|
|
80
|
+
return new TablePresentation(registry, model);
|
|
81
|
+
}, [registry, model]);
|
|
85
82
|
|
|
86
83
|
return { model, presentation };
|
|
87
84
|
};
|
|
88
85
|
|
|
89
86
|
const DefaultStory = () => {
|
|
90
|
-
const client = useClient();
|
|
91
87
|
const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
|
|
92
88
|
const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
|
|
93
|
-
const { space } =
|
|
89
|
+
const { space } = useClientStory();
|
|
94
90
|
|
|
95
91
|
const handleCreate = useCallback(
|
|
96
|
-
(schema:
|
|
97
|
-
|
|
92
|
+
(schema: Type.AnyEntity, values: any) => {
|
|
93
|
+
invariant(Type.isObjectSchema(schema));
|
|
94
|
+
invariant(space);
|
|
95
|
+
return space.db.add(Obj.make(schema, values));
|
|
98
96
|
},
|
|
99
97
|
[space],
|
|
100
98
|
);
|
|
101
99
|
|
|
102
100
|
return (
|
|
103
|
-
<div className='
|
|
101
|
+
<div className='dx-expander grid grid-cols-2 divide-x divide-separator'>
|
|
104
102
|
<TableComponent.Root>
|
|
105
|
-
<TableComponent.
|
|
103
|
+
<TableComponent.Content
|
|
106
104
|
model={orgModel}
|
|
107
105
|
schema={Organization.Organization}
|
|
108
106
|
presentation={orgPresentation}
|
|
109
107
|
onCreate={handleCreate}
|
|
110
|
-
client={client}
|
|
111
108
|
ignoreAttention
|
|
112
109
|
testId='relations-0'
|
|
113
110
|
/>
|
|
114
111
|
</TableComponent.Root>
|
|
115
112
|
<TableComponent.Root>
|
|
116
|
-
<TableComponent.
|
|
113
|
+
<TableComponent.Content
|
|
117
114
|
model={contactModel}
|
|
118
115
|
schema={Person.Person}
|
|
119
116
|
presentation={contactPresentation}
|
|
120
117
|
onCreate={handleCreate}
|
|
121
|
-
client={client}
|
|
122
118
|
ignoreAttention
|
|
123
119
|
testId='relations-1'
|
|
124
120
|
/>
|
|
@@ -131,8 +127,9 @@ const meta = {
|
|
|
131
127
|
title: 'ui/react-ui-table/Relations',
|
|
132
128
|
render: DefaultStory,
|
|
133
129
|
decorators: [
|
|
134
|
-
withTheme,
|
|
135
|
-
|
|
130
|
+
withTheme(),
|
|
131
|
+
withRegistry,
|
|
132
|
+
// TODO(thure): Shouldn't `layout: 'fullscreen'` below make this unnecessary?
|
|
136
133
|
withLayout({ classNames: 'fixed inset-0' }),
|
|
137
134
|
withClientProvider({
|
|
138
135
|
types: [View.View, Organization.Organization, Person.Person, Table.Table],
|
|
@@ -152,7 +149,7 @@ export default meta;
|
|
|
152
149
|
type Story = StoryObj<typeof meta>;
|
|
153
150
|
|
|
154
151
|
export const Default: Story = {
|
|
155
|
-
play: async ({ canvasElement }
|
|
152
|
+
play: async ({ canvasElement }) => {
|
|
156
153
|
const canvas = within(canvasElement);
|
|
157
154
|
const body = within(document.body);
|
|
158
155
|
|
|
@@ -200,8 +197,15 @@ export const Default: Story = {
|
|
|
200
197
|
const saveButton = await body.findByTestId('save-button');
|
|
201
198
|
await userEvent.click(saveButton);
|
|
202
199
|
|
|
203
|
-
// Verify the relation was set (cell should now contain the org name)
|
|
204
|
-
|
|
200
|
+
// Verify the relation was set (cell should now contain the org name).
|
|
201
|
+
// Wait for the grid (Lit web component) to repaint after the reactive data update.
|
|
202
|
+
await waitFor(
|
|
203
|
+
async () => {
|
|
204
|
+
const updatedCell = within(secondGrid).getByTestId('grid.4.0');
|
|
205
|
+
await expect(updatedCell).toHaveTextContent(orgName.substring(0, 4));
|
|
206
|
+
},
|
|
207
|
+
{ timeout: 5000 },
|
|
208
|
+
);
|
|
205
209
|
|
|
206
210
|
// Test object creation (new relations) - equivalent to "new relations work as expected" test
|
|
207
211
|
// Find a different cell to test object creation (second row, relations column)
|
|
@@ -220,23 +224,24 @@ export const Default: Story = {
|
|
|
220
224
|
const newSearchField = await body.findByPlaceholderText('Search…');
|
|
221
225
|
await userEvent.click(newSearchField);
|
|
222
226
|
|
|
223
|
-
// Type a new object name (this will create a new object)
|
|
227
|
+
// Type a new object name (this will create a new object).
|
|
224
228
|
const newOrgName = 'Salieri LLC';
|
|
225
229
|
await userEvent.type(newSearchField, newOrgName);
|
|
226
230
|
|
|
227
|
-
//
|
|
228
|
-
const
|
|
229
|
-
await
|
|
230
|
-
|
|
231
|
-
// Press Enter to select/create
|
|
232
|
-
await userEvent.keyboard('{Enter}');
|
|
231
|
+
// Click the create option directly to open the create form.
|
|
232
|
+
const createOptionLabel = await body.findByText(/Create new object/, undefined, { timeout: 2000 });
|
|
233
|
+
await userEvent.click(createOptionLabel.closest('[role="option"]') as HTMLElement);
|
|
233
234
|
|
|
234
|
-
// Look for and click save button
|
|
235
|
-
const createReferencedObjectForm = await body.findByTestId('create-referenced-object-form'
|
|
235
|
+
// Look for and click save button.
|
|
236
|
+
const createReferencedObjectForm = await body.findByTestId('create-referenced-object-form', undefined, {
|
|
237
|
+
timeout: 2000,
|
|
238
|
+
});
|
|
236
239
|
const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
|
|
240
|
+
await expect(saveObjectButton).not.toBeDisabled();
|
|
237
241
|
await userEvent.click(saveObjectButton);
|
|
238
242
|
|
|
239
|
-
// Verify the
|
|
240
|
-
await
|
|
243
|
+
// Verify the relation was set by checking for the accessory link anchor in the cell.
|
|
244
|
+
const updatedNewCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 5000 });
|
|
245
|
+
await expect(updatedNewCell.querySelector('dx-anchor')).toBeTruthy();
|
|
241
246
|
},
|
|
242
|
-
}
|
|
247
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
5
6
|
import React from 'react';
|
|
6
7
|
|
|
7
8
|
import { DropdownMenu, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
@@ -13,8 +14,8 @@ type RowActionsMenuProps = { model: TableModel; modals: ModalController };
|
|
|
13
14
|
|
|
14
15
|
export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
|
|
15
16
|
const { t } = useTranslation(translationKey);
|
|
16
|
-
const hasSelection = model.selection.hasSelection
|
|
17
|
-
const state = modals.state
|
|
17
|
+
const hasSelection = model.selection.hasSelection;
|
|
18
|
+
const state = useAtomValue(modals.state);
|
|
18
19
|
if (state?.type !== 'row') {
|
|
19
20
|
return null;
|
|
20
21
|
}
|
|
@@ -6,41 +6,34 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import React, { useCallback } from 'react';
|
|
8
8
|
|
|
9
|
-
import { Obj, type QueryAST, Type } from '@dxos/echo';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
FormatAnnotation,
|
|
13
|
-
FormatEnum,
|
|
14
|
-
GeneratorAnnotation,
|
|
15
|
-
LabelAnnotation,
|
|
16
|
-
PropertyMetaAnnotationId,
|
|
17
|
-
} from '@dxos/echo/internal';
|
|
18
|
-
import { live } from '@dxos/echo/internal';
|
|
9
|
+
import { Annotation, type Database, Format, Obj, type QueryAST, Ref, Type } from '@dxos/echo';
|
|
10
|
+
import { View } from '@dxos/echo';
|
|
11
|
+
import { type Mutable, PropertyMetaAnnotationId } from '@dxos/echo/internal';
|
|
19
12
|
import { invariant } from '@dxos/invariant';
|
|
20
13
|
import { faker } from '@dxos/random';
|
|
21
14
|
import { PublicKey } from '@dxos/react-client';
|
|
22
|
-
import { type Space } from '@dxos/react-client/echo';
|
|
23
15
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
16
|
+
import { Panel, ScrollArea } from '@dxos/react-ui';
|
|
17
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
18
|
+
import { ViewEditor, translations as formTranslations } from '@dxos/react-ui-form';
|
|
26
19
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
20
|
+
import { ViewModel, getSchemaFromPropertyDefinitions, getTypenameFromQuery } from '@dxos/schema';
|
|
21
|
+
import { TestSchema, createObjectFactory } from '@dxos/schema/testing';
|
|
22
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
29
23
|
|
|
30
24
|
import { useTestTableModel } from '../../testing';
|
|
31
25
|
import { translations } from '../../translations';
|
|
32
26
|
import { Table } from '../../types';
|
|
33
|
-
import { TableToolbar } from '../TableToolbar';
|
|
34
27
|
|
|
35
28
|
import { Table as TableComponent } from './Table';
|
|
36
29
|
|
|
37
|
-
const
|
|
30
|
+
const Example = Schema.Struct({
|
|
38
31
|
// TODO(wittjosiah): Should be title. Currently name to work with default label.
|
|
39
32
|
name: Schema.optional(Schema.String).annotations({ title: 'Title' }),
|
|
40
33
|
urgent: Schema.optional(Schema.Boolean).annotations({ title: 'Urgent' }),
|
|
41
34
|
status: Schema.optional(
|
|
42
35
|
Schema.Literal('todo', 'in-progress', 'done')
|
|
43
|
-
.pipe(FormatAnnotation.set(
|
|
36
|
+
.pipe(Format.FormatAnnotation.set(Format.TypeFormat.SingleSelect))
|
|
44
37
|
.annotations({
|
|
45
38
|
title: 'Status',
|
|
46
39
|
[PropertyMetaAnnotationId]: {
|
|
@@ -55,24 +48,24 @@ const TestSchema = Schema.Struct({
|
|
|
55
48
|
}),
|
|
56
49
|
),
|
|
57
50
|
description: Schema.optional(Schema.String).annotations({ title: 'Description' }),
|
|
58
|
-
parent: Schema.optional(Schema.suspend(():
|
|
51
|
+
parent: Schema.optional(Schema.suspend((): Ref.RefSchema<Example> => Ref.Ref(Example))).annotations({
|
|
59
52
|
title: 'Parent',
|
|
60
53
|
}),
|
|
61
54
|
}).pipe(
|
|
62
|
-
Type.
|
|
63
|
-
LabelAnnotation.set(['name']),
|
|
55
|
+
Type.object({ typename: `com.example.type.${PublicKey.random().truncate()}`, version: '0.1.0' }),
|
|
56
|
+
Annotation.LabelAnnotation.set(['name']),
|
|
64
57
|
);
|
|
65
|
-
interface
|
|
58
|
+
interface Example extends Schema.Schema.Type<typeof Example> {}
|
|
66
59
|
|
|
67
60
|
const StoryViewEditor = ({
|
|
68
61
|
view,
|
|
69
62
|
schema,
|
|
70
|
-
|
|
63
|
+
db,
|
|
71
64
|
handleDeleteColumn,
|
|
72
65
|
}: {
|
|
73
66
|
view?: View.View;
|
|
74
67
|
schema?: Schema.Schema.AnyNoContext;
|
|
75
|
-
|
|
68
|
+
db?: Database.Database;
|
|
76
69
|
handleDeleteColumn: (fieldId: string) => void;
|
|
77
70
|
}) => {
|
|
78
71
|
const handleQueryChanged = useCallback(
|
|
@@ -81,7 +74,9 @@ const StoryViewEditor = ({
|
|
|
81
74
|
invariant(Type.isMutable(schema));
|
|
82
75
|
schema.updateTypename(getTypenameFromQuery(newQuery));
|
|
83
76
|
invariant(view);
|
|
84
|
-
view
|
|
77
|
+
Obj.change(view, (obj) => {
|
|
78
|
+
obj.query.ast = newQuery as Mutable<typeof newQuery>;
|
|
79
|
+
});
|
|
85
80
|
},
|
|
86
81
|
[schema, view],
|
|
87
82
|
);
|
|
@@ -92,7 +87,7 @@ const StoryViewEditor = ({
|
|
|
92
87
|
|
|
93
88
|
return (
|
|
94
89
|
<ViewEditor
|
|
95
|
-
registry={
|
|
90
|
+
registry={db?.schemaRegistry}
|
|
96
91
|
schema={schema}
|
|
97
92
|
view={view}
|
|
98
93
|
onQueryChanged={handleQueryChanged}
|
|
@@ -106,74 +101,87 @@ const StoryViewEditor = ({
|
|
|
106
101
|
//
|
|
107
102
|
|
|
108
103
|
const DefaultStory = () => {
|
|
109
|
-
const {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!schema || !view) {
|
|
104
|
+
const { db, schema, table, tableRef, model, presentation, handleInsertRow, handleSaveView, handleDeleteColumn } =
|
|
105
|
+
useTestTableModel();
|
|
106
|
+
|
|
107
|
+
const handleRowClick = useCallback(
|
|
108
|
+
(row: any) => {
|
|
109
|
+
if (model?.getDraftRowCount() === 0 && ['frozenRowsEnd', 'fixedEndStart', 'fixedEndEnd'].includes(row?.plane)) {
|
|
110
|
+
handleInsertRow();
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[model, handleInsertRow],
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (!schema || !table?.view.target) {
|
|
123
117
|
return <div />;
|
|
124
118
|
}
|
|
125
119
|
|
|
126
120
|
return (
|
|
127
121
|
<div className='grow grid grid-cols-[1fr_350px]'>
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
122
|
+
<TableComponent.Root ref={tableRef}>
|
|
123
|
+
<Panel.Root>
|
|
124
|
+
<Panel.Toolbar asChild>
|
|
125
|
+
<TableComponent.Toolbar
|
|
126
|
+
classNames='border-b border-subdued-separator'
|
|
127
|
+
onAdd={handleInsertRow}
|
|
128
|
+
onSave={handleSaveView}
|
|
129
|
+
/>
|
|
130
|
+
</Panel.Toolbar>
|
|
131
|
+
<Panel.Content asChild>
|
|
132
|
+
<TableComponent.Content
|
|
133
|
+
schema={schema}
|
|
134
|
+
model={model}
|
|
135
|
+
presentation={presentation}
|
|
136
|
+
onRowClick={handleRowClick}
|
|
137
|
+
ignoreAttention
|
|
138
|
+
/>
|
|
139
|
+
</Panel.Content>
|
|
140
|
+
</Panel.Root>
|
|
141
|
+
</TableComponent.Root>
|
|
142
|
+
<ScrollArea.Root orientation='vertical' classNames='border-l border-separator'>
|
|
143
|
+
<ScrollArea.Viewport>
|
|
144
|
+
<StoryViewEditor view={table.view.target} schema={schema} db={db} handleDeleteColumn={handleDeleteColumn} />
|
|
145
|
+
<SyntaxHighlighter language='json' className='text-xs'>
|
|
146
|
+
{JSON.stringify({ view: table.view.target, schema }, null, 2)}
|
|
147
|
+
</SyntaxHighlighter>
|
|
148
|
+
</ScrollArea.Viewport>
|
|
149
|
+
</ScrollArea.Root>
|
|
147
150
|
</div>
|
|
148
151
|
);
|
|
149
152
|
};
|
|
150
153
|
|
|
151
|
-
type
|
|
154
|
+
type DefaultStoryProps = { rows?: number };
|
|
152
155
|
|
|
153
156
|
//
|
|
154
157
|
// Story definitions.
|
|
155
158
|
//
|
|
156
159
|
|
|
157
|
-
// TODO(burdon): Need
|
|
160
|
+
// TODO(burdon): Need simpler story.
|
|
158
161
|
|
|
159
162
|
const meta = {
|
|
160
163
|
title: 'ui/react-ui-table/Table',
|
|
161
164
|
render: DefaultStory,
|
|
162
165
|
decorators: [
|
|
163
|
-
withTheme,
|
|
166
|
+
withTheme(),
|
|
167
|
+
withLayout({ layout: 'fullscreen' }),
|
|
168
|
+
withRegistry,
|
|
164
169
|
withClientProvider({
|
|
165
170
|
types: [View.View, Table.Table],
|
|
166
171
|
createIdentity: true,
|
|
167
172
|
createSpace: true,
|
|
168
|
-
onCreateSpace: async ({
|
|
169
|
-
const [schema] = await space.db.schemaRegistry.register([
|
|
170
|
-
const { view } = await
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
onCreateSpace: async ({ space }) => {
|
|
174
|
+
const [schema] = await space.db.schemaRegistry.register([Example]);
|
|
175
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: schema.typename });
|
|
176
|
+
const table = Table.make({ view, jsonSchema });
|
|
177
|
+
Obj.change(view, (obj) => {
|
|
178
|
+
obj.projection.fields = [
|
|
179
|
+
obj.projection.fields.find((field) => field.path === 'name')!,
|
|
180
|
+
...obj.projection.fields.filter((field) => field.path !== 'name'),
|
|
181
|
+
];
|
|
182
|
+
});
|
|
175
183
|
|
|
176
|
-
space.db.add(
|
|
184
|
+
space.db.add(table);
|
|
177
185
|
|
|
178
186
|
Array.from({ length: 10 }).map(() => {
|
|
179
187
|
return space.db.add(
|
|
@@ -192,7 +200,7 @@ const meta = {
|
|
|
192
200
|
controls: {
|
|
193
201
|
disable: true,
|
|
194
202
|
},
|
|
195
|
-
translations,
|
|
203
|
+
translations: [...translations, ...formTranslations],
|
|
196
204
|
},
|
|
197
205
|
} satisfies Meta<typeof TableComponent>;
|
|
198
206
|
|
|
@@ -209,26 +217,30 @@ export const StaticSchema: StoryObj = {
|
|
|
209
217
|
types: [View.View, Table.Table],
|
|
210
218
|
createIdentity: true,
|
|
211
219
|
createSpace: true,
|
|
212
|
-
onCreateSpace: async ({
|
|
213
|
-
const { view } = await
|
|
214
|
-
|
|
220
|
+
onCreateSpace: async ({ space }) => {
|
|
221
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({
|
|
222
|
+
db: space.db,
|
|
223
|
+
typename: TestSchema.Person.typename,
|
|
224
|
+
});
|
|
225
|
+
const table = Table.make({ view, jsonSchema });
|
|
226
|
+
space.db.add(table);
|
|
215
227
|
|
|
216
228
|
const factory = createObjectFactory(space.db, faker as any);
|
|
217
229
|
await factory([
|
|
218
|
-
{ type:
|
|
219
|
-
// { type:
|
|
230
|
+
{ type: TestSchema.Person, count: 10 },
|
|
231
|
+
// { type: TestSchema.Organization, count: 1 },
|
|
220
232
|
]);
|
|
221
233
|
},
|
|
222
234
|
}),
|
|
223
235
|
],
|
|
224
236
|
parameters: {
|
|
225
237
|
layout: 'fullscreen',
|
|
226
|
-
translations,
|
|
238
|
+
translations: [...translations, ...formTranslations],
|
|
227
239
|
},
|
|
228
240
|
};
|
|
229
241
|
|
|
230
242
|
const ContactWithArrayOfEmails = Schema.Struct({
|
|
231
|
-
name: Schema.String.pipe(GeneratorAnnotation.set('person.fullName')),
|
|
243
|
+
name: Schema.String.pipe(Annotation.GeneratorAnnotation.set('person.fullName')),
|
|
232
244
|
emails: Schema.optional(
|
|
233
245
|
Schema.Array(
|
|
234
246
|
Schema.Struct({
|
|
@@ -238,8 +250,8 @@ const ContactWithArrayOfEmails = Schema.Struct({
|
|
|
238
250
|
),
|
|
239
251
|
),
|
|
240
252
|
}).pipe(
|
|
241
|
-
|
|
242
|
-
typename: 'dxos.
|
|
253
|
+
Type.object({
|
|
254
|
+
typename: 'org.dxos.type.contact-with-array-of-emails',
|
|
243
255
|
version: '0.1.0',
|
|
244
256
|
}),
|
|
245
257
|
);
|
|
@@ -248,17 +260,21 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
248
260
|
render: DefaultStory,
|
|
249
261
|
decorators: [
|
|
250
262
|
withClientProvider({
|
|
251
|
-
types: [View.View, Table.Table,
|
|
263
|
+
types: [View.View, Table.Table, TestSchema.Person, TestSchema.Organization, ContactWithArrayOfEmails],
|
|
252
264
|
createIdentity: true,
|
|
253
265
|
createSpace: true,
|
|
254
|
-
onCreateSpace: async ({
|
|
255
|
-
const { view } = await
|
|
256
|
-
|
|
266
|
+
onCreateSpace: async ({ space }) => {
|
|
267
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({
|
|
268
|
+
db: space.db,
|
|
269
|
+
typename: ContactWithArrayOfEmails.typename,
|
|
270
|
+
});
|
|
271
|
+
const table = Table.make({ view, jsonSchema });
|
|
272
|
+
space.db.add(table);
|
|
257
273
|
|
|
258
274
|
const factory = createObjectFactory(space.db, faker as any);
|
|
259
275
|
await factory([
|
|
260
|
-
// { type:
|
|
261
|
-
// { type:
|
|
276
|
+
// { type: TestSchema.Person, count: 10 },
|
|
277
|
+
// { type: TestSchema.Organization, count: 1 },
|
|
262
278
|
{ type: ContactWithArrayOfEmails, count: 10 },
|
|
263
279
|
]);
|
|
264
280
|
},
|
|
@@ -270,7 +286,7 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
270
286
|
},
|
|
271
287
|
};
|
|
272
288
|
|
|
273
|
-
export const Tags: Meta<
|
|
289
|
+
export const Tags: Meta<DefaultStoryProps> = {
|
|
274
290
|
title: 'ui/react-ui-table/Table',
|
|
275
291
|
render: DefaultStory,
|
|
276
292
|
decorators: [
|
|
@@ -278,9 +294,9 @@ export const Tags: Meta<StoryProps> = {
|
|
|
278
294
|
types: [View.View, Table.Table],
|
|
279
295
|
createIdentity: true,
|
|
280
296
|
createSpace: true,
|
|
281
|
-
onCreateSpace: async ({
|
|
297
|
+
onCreateSpace: async ({ space }) => {
|
|
282
298
|
// Configure schema.
|
|
283
|
-
const typename = 'example.
|
|
299
|
+
const typename = 'com.example.type.single-select';
|
|
284
300
|
const selectOptions = [
|
|
285
301
|
{ id: 'one', title: 'One', color: 'emerald' },
|
|
286
302
|
{ id: 'two', title: 'Two', color: 'blue' },
|
|
@@ -294,25 +310,26 @@ export const Tags: Meta<StoryProps> = {
|
|
|
294
310
|
const schema = getSchemaFromPropertyDefinitions(typename, [
|
|
295
311
|
{
|
|
296
312
|
name: 'single',
|
|
297
|
-
format:
|
|
313
|
+
format: Format.TypeFormat.SingleSelect,
|
|
298
314
|
config: { options: selectOptions },
|
|
299
315
|
},
|
|
300
316
|
{
|
|
301
317
|
name: 'multiple',
|
|
302
|
-
format:
|
|
318
|
+
format: Format.TypeFormat.MultiSelect,
|
|
303
319
|
config: { options: selectOptions },
|
|
304
320
|
},
|
|
305
321
|
]);
|
|
306
322
|
const [storedSchema] = await space.db.schemaRegistry.register([schema]);
|
|
307
323
|
|
|
308
324
|
// Initialize table.
|
|
309
|
-
const { view } = await
|
|
310
|
-
|
|
325
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename });
|
|
326
|
+
const table = Table.make({ view, jsonSchema });
|
|
327
|
+
space.db.add(table);
|
|
311
328
|
|
|
312
329
|
// Populate.
|
|
313
330
|
Array.from({ length: 10 }).map(() => {
|
|
314
331
|
return space.db.add(
|
|
315
|
-
|
|
332
|
+
Obj.make(storedSchema, {
|
|
316
333
|
single: faker.helpers.arrayElement([...selectOptionIds, undefined]),
|
|
317
334
|
multiple: faker.helpers.randomSubset(selectOptionIds),
|
|
318
335
|
}),
|