@dxos/react-ui-table 0.8.4-main.9735255 → 0.8.4-main.abd8ff62ef
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-OZBLY7XM.mjs → chunk-FBURZBPH.mjs} +17 -23
- package/dist/lib/browser/chunk-FBURZBPH.mjs.map +7 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-YGC7LZJN.mjs} +39 -90
- package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +198 -235
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +7 -6
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/translations.mjs +36 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -1
- package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-DKKMSVKP.mjs} +17 -23
- package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-GKIWDEZ7.mjs} +39 -90
- package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +198 -235
- 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 +7 -6
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/translations.mjs +37 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +20 -22
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +82 -82
- 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 +20 -21
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +84 -84
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +17 -0
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +22 -24
- 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.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +7 -6
- package/dist/types/src/model/table-model.d.ts.map +1 -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/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +19 -21
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +1 -6
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +6 -5
- 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/query-ast.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/src/util/signals.d.ts.map +1 -1
- package/dist/types/src/util/sort.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +20 -20
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +4 -4
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/tag.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +59 -62
- package/src/components/Table/ColumnActionsMenu.tsx +7 -6
- package/src/components/Table/ColumnSettings.tsx +3 -4
- package/src/components/Table/DynamicTable.stories.tsx +19 -18
- package/src/components/Table/DynamicTable.tsx +13 -20
- package/src/components/Table/Relations.stories.tsx +78 -34
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +72 -52
- package/src/components/Table/Table.tsx +41 -435
- package/src/components/Table/TableContent.tsx +421 -0
- package/src/components/{TableToolbar → Table}/TableToolbar.tsx +36 -43
- package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
- package/src/components/TableCellEditor/FormCellEditor.tsx +14 -7
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
- package/src/components/TableCellEditor/TableCellEditor.tsx +12 -10
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +10 -10
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useProjectionModel.ts +3 -3
- package/src/hooks/useTableModel.ts +1 -1
- package/src/index.ts +0 -4
- package/src/model/table-model.test.ts +5 -6
- package/src/model/table-model.ts +18 -17
- package/src/model/table-presentation.test.ts +13 -7
- package/src/model/table-presentation.ts +12 -12
- package/src/playwright/TableManager.ts +10 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +48 -5
- package/src/testing/useTestTableModel.ts +6 -6
- package/src/translations.ts +19 -21
- package/src/types/Table.ts +12 -23
- package/src/util/dynamic-table.test.ts +77 -0
- package/src/util/dynamic-table.ts +17 -16
- package/src/util/query-ast.ts +3 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WNZSKIRF.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/src/components/TableToolbar/index.ts +0 -5
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { expect, test } from '@playwright/test';
|
|
6
6
|
|
|
7
7
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
8
|
-
import {
|
|
8
|
+
import { random } from '@dxos/random';
|
|
9
9
|
import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
|
|
10
10
|
|
|
11
11
|
import { TableManager } from './TableManager';
|
|
@@ -13,7 +13,7 @@ import { TableManager } from './TableManager';
|
|
|
13
13
|
const storyUrl = storybookUrl('ui-react-ui-table-table--default', 9004);
|
|
14
14
|
const relationsStoryUrl = storybookUrl('ui-react-ui-table-relations--default', 9004);
|
|
15
15
|
|
|
16
|
-
// NOTE(ZaymonFC): This test suite relies on the
|
|
16
|
+
// NOTE(ZaymonFC): This test suite relies on the random seed being set to 0 in the story.
|
|
17
17
|
test.describe('Table', () => {
|
|
18
18
|
test('Loads', async ({ browser, browserName }) => {
|
|
19
19
|
test.skip(browserName === 'webkit');
|
|
@@ -121,8 +121,22 @@ test.describe('Table', () => {
|
|
|
121
121
|
await page.close();
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
test('add column without changing format', async ({ browser, browserName }) => {
|
|
125
|
+
test.skip(browserName === 'webkit');
|
|
126
|
+
test.skip(browserName === 'firefox');
|
|
127
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
128
|
+
const table = new TableManager(page);
|
|
129
|
+
|
|
130
|
+
await table.grid.ready();
|
|
131
|
+
const newColumnLabel = 'DEFAULT FORMAT COL';
|
|
132
|
+
|
|
133
|
+
await table.addColumn({ label: newColumnLabel });
|
|
134
|
+
|
|
135
|
+
await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
|
|
136
|
+
await page.close();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('add column with format', async ({ browser, browserName }) => {
|
|
126
140
|
test.skip(browserName === 'webkit');
|
|
127
141
|
test.skip(browserName === 'firefox');
|
|
128
142
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
@@ -160,6 +174,35 @@ test.describe('Table', () => {
|
|
|
160
174
|
await page.close();
|
|
161
175
|
});
|
|
162
176
|
|
|
177
|
+
test('add row and edit cell', async ({ browser, browserName }) => {
|
|
178
|
+
test.skip(browserName === 'webkit');
|
|
179
|
+
test.skip(browserName === 'firefox');
|
|
180
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
181
|
+
const table = new TableManager(page);
|
|
182
|
+
|
|
183
|
+
await table.grid.ready();
|
|
184
|
+
|
|
185
|
+
// Click the CTA row at the bottom to add a new row.
|
|
186
|
+
await table.grid.cell(0, 0, 'frozenRowsEnd').click();
|
|
187
|
+
|
|
188
|
+
// All schema fields are optional so the row is added directly to the grid (not as a draft).
|
|
189
|
+
const newCell = table.grid.cell(0, 10, 'grid');
|
|
190
|
+
await newCell.waitFor({ state: 'visible' });
|
|
191
|
+
|
|
192
|
+
// Focus should already be on the new row; engage edit mode.
|
|
193
|
+
await page.keyboard.press('Enter');
|
|
194
|
+
await page.getByTestId('grid.cell-editor').waitFor({ state: 'visible' });
|
|
195
|
+
|
|
196
|
+
// Type text and confirm.
|
|
197
|
+
const cellContent = 'New row content';
|
|
198
|
+
await page.keyboard.type(cellContent);
|
|
199
|
+
await page.keyboard.press('Enter');
|
|
200
|
+
|
|
201
|
+
// Assert the cell content is saved.
|
|
202
|
+
await expect(newCell).toHaveText(cellContent);
|
|
203
|
+
await page.close();
|
|
204
|
+
});
|
|
205
|
+
|
|
163
206
|
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
164
207
|
test.skip('extant relations work as expected', async ({ browser, browserName }) => {
|
|
165
208
|
test.skip(browserName === 'webkit');
|
|
@@ -267,7 +310,7 @@ test.describe('Table', () => {
|
|
|
267
310
|
dxGridElement.scrollToColumn(6);
|
|
268
311
|
});
|
|
269
312
|
|
|
270
|
-
const nonRefContent =
|
|
313
|
+
const nonRefContent = random.lorem.words(3);
|
|
271
314
|
const nonRefCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="6"]');
|
|
272
315
|
await nonRefCell.click();
|
|
273
316
|
await page.keyboard.press('Enter');
|
|
@@ -7,10 +7,10 @@ import { type RefObject, useCallback, useContext, useMemo, useRef } from 'react'
|
|
|
7
7
|
|
|
8
8
|
import { type Database, type Type } from '@dxos/echo';
|
|
9
9
|
import { isMutable } from '@dxos/echo/internal';
|
|
10
|
-
import {
|
|
10
|
+
import { random } from '@dxos/random';
|
|
11
11
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
12
|
import { useClientStory } from '@dxos/react-client/testing';
|
|
13
|
-
import { useGlobalFilteredObjects } from '@dxos/react-ui-
|
|
13
|
+
import { useGlobalFilteredObjects } from '@dxos/react-ui-search';
|
|
14
14
|
import { type ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
|
|
15
15
|
|
|
16
16
|
import { type TableController } from '../components';
|
|
@@ -18,9 +18,9 @@ import { useAddRow, useProjectionModel, useTableModel } from '../hooks';
|
|
|
18
18
|
import { type TableModel, TablePresentation } from '../model';
|
|
19
19
|
import { Table } from '../types';
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
random.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
|
|
22
22
|
|
|
23
|
-
export type TestTableModel<T extends Type.
|
|
23
|
+
export type TestTableModel<T extends Type.AnyObj = Type.AnyObj> = {
|
|
24
24
|
schema: T | undefined;
|
|
25
25
|
table: Table.Table | undefined;
|
|
26
26
|
projection: ProjectionModel | undefined;
|
|
@@ -38,7 +38,7 @@ export type TestTableModel<T extends Type.Obj.Any = Type.Obj.Any> = {
|
|
|
38
38
|
* Custom hook to create and manage a test table model for storybook demonstrations.
|
|
39
39
|
* Provides table data, schema, and handlers for table operations.
|
|
40
40
|
*/
|
|
41
|
-
export const useTestTableModel = <T extends Type.
|
|
41
|
+
export const useTestTableModel = <T extends Type.AnyObj = Type.AnyObj>(): TestTableModel<T> => {
|
|
42
42
|
const registry = useContext(RegistryContext);
|
|
43
43
|
const { space } = useClientStory();
|
|
44
44
|
const db = space?.db;
|
|
@@ -97,7 +97,7 @@ export const useTestTableModel = <T extends Type.Obj.Any = Type.Obj.Any>(): Test
|
|
|
97
97
|
rows: filteredObjects,
|
|
98
98
|
onInsertRow: addRow,
|
|
99
99
|
onDeleteRows: handleDeleteRows,
|
|
100
|
-
|
|
100
|
+
onColumnDelete: handleDeleteColumn,
|
|
101
101
|
onCellUpdate: handleCellUpdate,
|
|
102
102
|
onRowOrderChange: handleRowOrderChange,
|
|
103
103
|
});
|
package/src/translations.ts
CHANGED
|
@@ -10,27 +10,25 @@ export const translations = [
|
|
|
10
10
|
{
|
|
11
11
|
'en-US': {
|
|
12
12
|
[translationKey]: {
|
|
13
|
-
'object
|
|
14
|
-
'create
|
|
15
|
-
'table
|
|
16
|
-
'settings
|
|
17
|
-
'table
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'column
|
|
24
|
-
'column
|
|
25
|
-
'column
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'create new object label': 'Create new object “{{text}}”',
|
|
33
|
-
'save view label': 'Save changes to view',
|
|
13
|
+
'object.placeholder': 'New table',
|
|
14
|
+
'create-object.label': 'Create table',
|
|
15
|
+
'table-name.placeholder': 'Table name',
|
|
16
|
+
'settings.title': 'Table settings',
|
|
17
|
+
'table-schema.label': 'Type',
|
|
18
|
+
'continue.label': 'Continue',
|
|
19
|
+
'create-stack-section.label': 'Create table',
|
|
20
|
+
'add-row.label': 'Add row',
|
|
21
|
+
'column-action-sort-descending.menu': 'Sort Descending',
|
|
22
|
+
'column-action-sort-ascending.menu': 'Sort Ascending',
|
|
23
|
+
'column-action-clear-sorting.menu': 'Clear Sorting',
|
|
24
|
+
'column-action-settings.menu': 'Column Settings',
|
|
25
|
+
'column-action-delete.menu': 'Delete Column',
|
|
26
|
+
'delete-row.label': 'Delete row',
|
|
27
|
+
'bulk-delete-row.label': 'Delete selected rows',
|
|
28
|
+
'column-deleted.label': 'Column deleted',
|
|
29
|
+
'new-column-button.label': 'Create column',
|
|
30
|
+
'create-new-object.label': 'Create new object “{{text}}”',
|
|
31
|
+
'save-view.label': 'Save changes to view',
|
|
34
32
|
},
|
|
35
33
|
},
|
|
36
34
|
},
|
package/src/types/Table.ts
CHANGED
|
@@ -6,14 +6,15 @@ import * as Match from 'effect/Match';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
8
8
|
|
|
9
|
-
import { Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
+
import { Annotation, JsonSchema, Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
+
import { View } from '@dxos/echo';
|
|
10
11
|
import { FormInputAnnotation, type JsonPath, type JsonSchemaType, LabelAnnotation } from '@dxos/echo/internal';
|
|
11
|
-
import {
|
|
12
|
+
import { ViewAnnotation } from '@dxos/schema';
|
|
12
13
|
|
|
13
14
|
export const Table = Schema.Struct({
|
|
14
15
|
name: Schema.String.pipe(Schema.optional),
|
|
15
16
|
|
|
16
|
-
view:
|
|
17
|
+
view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false)),
|
|
17
18
|
|
|
18
19
|
sizes: Schema.Record({
|
|
19
20
|
// TODO(wittjosiah): Should be JsonPath.
|
|
@@ -22,11 +23,15 @@ export const Table = Schema.Struct({
|
|
|
22
23
|
}).pipe(Schema.mutable, FormInputAnnotation.set(false)),
|
|
23
24
|
}).pipe(
|
|
24
25
|
Type.object({
|
|
25
|
-
typename: 'dxos.
|
|
26
|
-
version: '0.
|
|
26
|
+
typename: 'org.dxos.type.table',
|
|
27
|
+
version: '0.1.0',
|
|
27
28
|
}),
|
|
28
29
|
LabelAnnotation.set(['name']),
|
|
29
|
-
ViewAnnotation.set(
|
|
30
|
+
ViewAnnotation.set(['view']),
|
|
31
|
+
Annotation.IconAnnotation.set({
|
|
32
|
+
icon: 'ph--table--regular',
|
|
33
|
+
hue: 'green',
|
|
34
|
+
}),
|
|
30
35
|
);
|
|
31
36
|
|
|
32
37
|
export interface Table extends Schema.Schema.Type<typeof Table> {}
|
|
@@ -47,7 +52,7 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
|
|
|
47
52
|
|
|
48
53
|
// Preset sizes.
|
|
49
54
|
if (jsonSchema) {
|
|
50
|
-
const schema =
|
|
55
|
+
const schema = JsonSchema.toEffectSchema(jsonSchema);
|
|
51
56
|
const properties = SchemaAST.getPropertySignatures(schema.ast);
|
|
52
57
|
for (const property of properties) {
|
|
53
58
|
const name = property.name.toString() as JsonPath;
|
|
@@ -72,19 +77,3 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
|
|
|
72
77
|
|
|
73
78
|
return table;
|
|
74
79
|
};
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
// V1
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
export const TableV1 = Schema.Struct({
|
|
81
|
-
sizes: Schema.Record({
|
|
82
|
-
key: Schema.String,
|
|
83
|
-
value: Schema.Number,
|
|
84
|
-
}).pipe(Schema.mutable),
|
|
85
|
-
}).pipe(
|
|
86
|
-
Type.object({
|
|
87
|
-
typename: 'dxos.org/type/Table',
|
|
88
|
-
version: '0.1.0',
|
|
89
|
-
}),
|
|
90
|
-
);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import { describe, expect, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { Format } from '@dxos/echo';
|
|
9
|
+
|
|
10
|
+
import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from './dynamic-table';
|
|
11
|
+
|
|
12
|
+
// Quarantined in CI only — flaky under the workflow's `--no-file-parallelism`
|
|
13
|
+
// runner / hoisted node_modules layout. Local runs still cover the suite so
|
|
14
|
+
// regressions surface during development.
|
|
15
|
+
// Trunk.io tracking:
|
|
16
|
+
// https://app.trunk.io/dxos/flaky-tests/test/fe424f97-f8a1-5f2f-a191-5b3ec4e8f6b7
|
|
17
|
+
// https://app.trunk.io/dxos/flaky-tests/test/ee00f293-9b9b-55ed-b4cd-48a07edea96c
|
|
18
|
+
const describeOutsideCI = process.env.CI ? describe.skip : describe;
|
|
19
|
+
describeOutsideCI('makeDynamicTable', () => {
|
|
20
|
+
/**
|
|
21
|
+
* Base case: plain jsonSchema (not from Echo / JsonSchema.toJsonSchema). Does not exercise the path
|
|
22
|
+
* where projection or schema are reactive, so this does not reproduce the Obj.update regression.
|
|
23
|
+
* Confirms makeDynamicTable + setProperties (title) works with plain objects.
|
|
24
|
+
*/
|
|
25
|
+
test('makeDynamicTable with plain jsonSchema and properties with title does not throw', () => {
|
|
26
|
+
const registry = Registry.make();
|
|
27
|
+
const jsonSchema = {
|
|
28
|
+
type: 'object' as const,
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: 'string' as const },
|
|
31
|
+
name: { type: 'string' as const },
|
|
32
|
+
},
|
|
33
|
+
required: ['id'],
|
|
34
|
+
};
|
|
35
|
+
const properties: TablePropertyDefinition[] = [
|
|
36
|
+
{ name: 'id', format: Format.TypeFormat.String },
|
|
37
|
+
{ name: 'name', format: Format.TypeFormat.String, title: 'Name' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
expect(() => {
|
|
41
|
+
const { projection, object } = makeDynamicTable({
|
|
42
|
+
registry,
|
|
43
|
+
jsonSchema,
|
|
44
|
+
properties,
|
|
45
|
+
});
|
|
46
|
+
expect(projection).toBeDefined();
|
|
47
|
+
expect(object).toBeDefined();
|
|
48
|
+
expect(projection.getFields().length).toBeGreaterThan(0);
|
|
49
|
+
}).not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Regression test for the path where jsonSchema comes from getBaseSchema(typename, properties).
|
|
54
|
+
* That path uses Echo (ViewModel.make, JsonSchema.toJsonSchema); mutations must run inside Obj.update and on a
|
|
55
|
+
* cloned schema. This test ensures that flow does not throw.
|
|
56
|
+
*/
|
|
57
|
+
test('makeDynamicTable with jsonSchema from getBaseSchema(typename, properties) and properties with title does not throw', () => {
|
|
58
|
+
const registry = Registry.make();
|
|
59
|
+
const typename = 'com.example.type.dynamicTableTest';
|
|
60
|
+
const properties: TablePropertyDefinition[] = [
|
|
61
|
+
{ name: 'id', format: Format.TypeFormat.String },
|
|
62
|
+
{ name: 'name', format: Format.TypeFormat.String, title: 'Name' },
|
|
63
|
+
];
|
|
64
|
+
const { jsonSchema } = getBaseSchema({ typename, properties });
|
|
65
|
+
|
|
66
|
+
expect(() => {
|
|
67
|
+
const { projection, object } = makeDynamicTable({
|
|
68
|
+
registry,
|
|
69
|
+
jsonSchema,
|
|
70
|
+
properties,
|
|
71
|
+
});
|
|
72
|
+
expect(projection).toBeDefined();
|
|
73
|
+
expect(object).toBeDefined();
|
|
74
|
+
expect(projection.getFields().length).toBeGreaterThan(0);
|
|
75
|
+
}).not.toThrow();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import type * as Types from 'effect/Types';
|
|
6
7
|
|
|
7
|
-
import { Filter,
|
|
8
|
+
import { Filter, JsonSchema, Obj, Order, Query, type QueryAST, Ref, Type } from '@dxos/echo';
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
8
10
|
import {
|
|
9
11
|
ProjectionModel,
|
|
10
12
|
type SchemaPropertyDefinition,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
ViewModel,
|
|
14
|
+
createEchoChangeCallback,
|
|
13
15
|
getSchemaFromPropertyDefinitions,
|
|
14
16
|
} from '@dxos/schema';
|
|
15
17
|
|
|
@@ -34,16 +36,16 @@ export const getBaseSchema = ({
|
|
|
34
36
|
properties,
|
|
35
37
|
jsonSchema,
|
|
36
38
|
}: {
|
|
37
|
-
schema?: Type.
|
|
39
|
+
schema?: Type.AnyEntity;
|
|
38
40
|
typename?: string;
|
|
39
41
|
properties?: TablePropertyDefinition[];
|
|
40
|
-
jsonSchema?: JsonSchema.JsonSchema
|
|
41
|
-
}): { typename: string; jsonSchema: JsonSchema.JsonSchema } => {
|
|
42
|
+
jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
|
|
43
|
+
}): { typename: string; jsonSchema: Types.DeepMutable<JsonSchema.JsonSchema> } => {
|
|
42
44
|
if (typename && properties) {
|
|
43
45
|
const schema = getSchemaFromPropertyDefinitions(typename, properties);
|
|
44
|
-
return { typename: schema.typename, jsonSchema: schema
|
|
46
|
+
return { typename: schema.typename, jsonSchema: JsonSchema.toJsonSchema(schema) };
|
|
45
47
|
} else if (schema) {
|
|
46
|
-
return { typename: Type.getTypename(schema)!, jsonSchema:
|
|
48
|
+
return { typename: Type.getTypename(schema)!, jsonSchema: JsonSchema.toJsonSchema(schema) };
|
|
47
49
|
} else if (typename && jsonSchema) {
|
|
48
50
|
return { typename, jsonSchema };
|
|
49
51
|
} else {
|
|
@@ -57,22 +59,21 @@ export const makeDynamicTable = ({
|
|
|
57
59
|
properties,
|
|
58
60
|
}: {
|
|
59
61
|
registry: Registry.Registry;
|
|
60
|
-
jsonSchema: JsonSchema.JsonSchema
|
|
62
|
+
jsonSchema: Types.DeepMutable<JsonSchema.JsonSchema>;
|
|
61
63
|
properties?: TablePropertyDefinition[];
|
|
62
64
|
}): { projection: ProjectionModel; object: Table.Table } => {
|
|
63
|
-
const view =
|
|
65
|
+
const view = ViewModel.make({
|
|
64
66
|
query: Query.select(Filter.everything()),
|
|
65
67
|
jsonSchema,
|
|
66
68
|
...(properties && { fields: properties.map((property) => property.name) }),
|
|
67
69
|
});
|
|
68
70
|
const object = Obj.make(Table.Table, { view: Ref.make(view), sizes: {} });
|
|
69
71
|
|
|
70
|
-
// Use direct change callback for in-memory objects (non-ECHO backed).
|
|
71
72
|
const projection = new ProjectionModel({
|
|
72
73
|
registry,
|
|
73
74
|
view,
|
|
74
75
|
baseSchema: jsonSchema,
|
|
75
|
-
change:
|
|
76
|
+
change: createEchoChangeCallback(view, jsonSchema),
|
|
76
77
|
});
|
|
77
78
|
projection.normalizeView();
|
|
78
79
|
if (properties && projection.getFields()) {
|
|
@@ -92,8 +93,8 @@ const setProperties = (
|
|
|
92
93
|
const field = projection.getFields().find((field) => field.path === property.name);
|
|
93
94
|
if (field) {
|
|
94
95
|
if (property.size !== undefined) {
|
|
95
|
-
Obj.
|
|
96
|
-
|
|
96
|
+
Obj.update(table, (table) => {
|
|
97
|
+
table.sizes[field.path] = property.size!;
|
|
97
98
|
});
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -110,9 +111,9 @@ const setProperties = (
|
|
|
110
111
|
const currentQuery = Query.fromAst(Obj.getSnapshot(view).query.ast);
|
|
111
112
|
// Use any type parameter since we're working with dynamic field paths
|
|
112
113
|
const newQuery = currentQuery.orderBy(Order.property<any>(field.path as string, property.sort));
|
|
113
|
-
Obj.
|
|
114
|
+
Obj.update(view, (view) => {
|
|
114
115
|
// Type assertion needed because Query AST types have some variance issues.
|
|
115
|
-
|
|
116
|
+
view.query.ast = newQuery.ast as typeof view.query.ast;
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
119
|
}
|
package/src/util/query-ast.ts
CHANGED
|
@@ -15,5 +15,8 @@ export const extractOrder = (queryAst: QueryAST.Query): readonly QueryAST.Order[
|
|
|
15
15
|
if (queryAst.type === 'options') {
|
|
16
16
|
return extractOrder(queryAst.query);
|
|
17
17
|
}
|
|
18
|
+
if (queryAst.type === 'from') {
|
|
19
|
+
return extractOrder(queryAst.query);
|
|
20
|
+
}
|
|
18
21
|
return undefined;
|
|
19
22
|
};
|
|
@@ -39,7 +39,7 @@ const createButton = ({
|
|
|
39
39
|
.map(([k, v]) => `${k}="${v}"`)
|
|
40
40
|
.join(' ');
|
|
41
41
|
|
|
42
|
-
return `<button ${attr} data-testid="${testId}" class="dx-button
|
|
42
|
+
return `<button ${attr} data-testid="${testId}" class="dx-button w-6 aspect-square px-0.5 min-h-0" ${dataAttrs} ${disabled ? 'disabled' : ''} data-dx-grid-action="accessory"><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
const addColumnButton = {
|
|
@@ -25,8 +25,8 @@ export const CONTROL_IDENTIFIERS = {
|
|
|
25
25
|
} as const;
|
|
26
26
|
|
|
27
27
|
const BASE_CLASSES = {
|
|
28
|
-
checkbox: 'absolute inset-
|
|
29
|
-
switch: 'absolute inset-
|
|
28
|
+
checkbox: 'absolute inset-y-[.375rem] end-[.375rem] dx-checkbox',
|
|
29
|
+
switch: 'absolute inset-y-[.25rem] end-[.25rem] dx-checkbox--switch',
|
|
30
30
|
} as const;
|
|
31
31
|
|
|
32
32
|
const renderAttributes = (data: Record<string, string>) => {
|