@dxos/react-ui-table 0.8.4-main.67995b8 → 0.8.4-main.c4373fc
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-4K7JPALT.mjs +66 -0
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5XLSHLOG.mjs → chunk-OZTXLZGM.mjs} +544 -491
- package/dist/lib/browser/chunk-OZTXLZGM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +584 -673
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +13 -8
- 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-MK6OVUF6.mjs +67 -0
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-Y66YLSTC.mjs → chunk-S56Z4V4X.mjs} +544 -491
- package/dist/lib/node-esm/chunk-S56Z4V4X.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +584 -673
- 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 +13 -8
- 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 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +51 -3
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +108 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +8 -4
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +42 -4
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -6
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +12 -17
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -4
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
- package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +3 -2
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +2 -2
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +0 -7
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +9 -7
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +2 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +2 -3
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +3 -22
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- 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/types/schema.d.ts +25 -5
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +4 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +59 -58
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +1 -1
- package/src/components/Table/DynamicTable.stories.tsx +15 -11
- package/src/components/Table/DynamicTable.tsx +8 -10
- package/src/components/Table/Relations.stories.tsx +248 -0
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +66 -54
- package/src/components/Table/Table.tsx +140 -115
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +153 -42
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -64
- package/src/components/TableCellEditor/TableCellEditor.tsx +74 -224
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +2 -2
- package/src/hooks/useAddRow.ts +7 -5
- package/src/hooks/useTableModel.ts +2 -2
- package/src/model/modal-controller.ts +1 -29
- package/src/model/selection-model.ts +3 -2
- package/src/model/table-model.test.ts +19 -10
- package/src/model/table-model.ts +37 -25
- package/src/model/table-presentation.test.ts +23 -21
- package/src/model/table-presentation.ts +74 -33
- package/src/model/table-sorting.ts +16 -7
- package/src/playwright/smoke.spec.ts +129 -37
- package/src/testing/useTestTableModel.ts +11 -7
- package/src/translations.ts +2 -2
- package/src/types/index.ts +1 -1
- package/src/types/schema.ts +63 -6
- package/src/util/dynamic-table.ts +13 -13
- package/src/util/index.ts +0 -1
- package/src/util/schema.ts +4 -4
- package/src/util/table-buttons.ts +4 -3
- package/src/util/table-controls.ts +4 -1
- package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +0 -7
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y66YLSTC.mjs.map +0 -7
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
- package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
- package/dist/types/src/util/table.d.ts +0 -11
- package/dist/types/src/util/table.d.ts.map +0 -1
- package/src/components/Table/CreateRefPanel.tsx +0 -86
- package/src/components/Table/relations.stories.tsx +0 -116
- package/src/components/TableCellEditor/extension.ts +0 -61
- package/src/util/table.ts +0 -49
|
@@ -4,16 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect, test } from '@playwright/test';
|
|
6
6
|
|
|
7
|
+
import { type DxGrid } from '@dxos/lit-grid';
|
|
8
|
+
import { faker } from '@dxos/random';
|
|
7
9
|
import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
|
|
8
10
|
|
|
9
11
|
import { TableManager } from './TableManager';
|
|
10
12
|
|
|
11
13
|
const storyUrl = storybookUrl('ui-react-ui-table-table--default', 9004);
|
|
14
|
+
const relationsStoryUrl = storybookUrl('ui-react-ui-table-relations--default', 9004);
|
|
12
15
|
|
|
13
16
|
// NOTE(ZaymonFC): This test suite relies on the faker seed being set to 0 in the story.
|
|
14
17
|
test.describe('Table', () => {
|
|
15
18
|
test('Loads', async ({ browser, browserName }) => {
|
|
16
19
|
test.skip(browserName === 'webkit');
|
|
20
|
+
test.skip(browserName === 'firefox');
|
|
17
21
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
18
22
|
const table = new TableManager(page);
|
|
19
23
|
|
|
@@ -23,6 +27,7 @@ test.describe('Table', () => {
|
|
|
23
27
|
|
|
24
28
|
test('sort', async ({ browser, browserName }) => {
|
|
25
29
|
test.skip(browserName === 'webkit');
|
|
30
|
+
test.skip(browserName === 'firefox');
|
|
26
31
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
27
32
|
const table = new TableManager(page);
|
|
28
33
|
|
|
@@ -39,6 +44,7 @@ test.describe('Table', () => {
|
|
|
39
44
|
|
|
40
45
|
test('selection', async ({ browser, browserName }) => {
|
|
41
46
|
test.skip(browserName === 'webkit');
|
|
47
|
+
test.skip(browserName === 'firefox');
|
|
42
48
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
43
49
|
const table = new TableManager(page);
|
|
44
50
|
|
|
@@ -60,6 +66,7 @@ test.describe('Table', () => {
|
|
|
60
66
|
|
|
61
67
|
test('delete row', async ({ browser, browserName }) => {
|
|
62
68
|
test.skip(browserName === 'webkit');
|
|
69
|
+
test.skip(browserName === 'firefox');
|
|
63
70
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
64
71
|
const table = new TableManager(page);
|
|
65
72
|
|
|
@@ -71,6 +78,7 @@ test.describe('Table', () => {
|
|
|
71
78
|
|
|
72
79
|
test('delete row--select all', async ({ browser, browserName }) => {
|
|
73
80
|
test.skip(browserName === 'webkit');
|
|
81
|
+
test.skip(browserName === 'firefox');
|
|
74
82
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
75
83
|
const table = new TableManager(page);
|
|
76
84
|
|
|
@@ -88,6 +96,7 @@ test.describe('Table', () => {
|
|
|
88
96
|
|
|
89
97
|
test('delete column', async ({ browser, browserName }) => {
|
|
90
98
|
test.skip(browserName === 'webkit');
|
|
99
|
+
test.skip(browserName === 'firefox');
|
|
91
100
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
92
101
|
const table = new TableManager(page);
|
|
93
102
|
|
|
@@ -114,6 +123,7 @@ test.describe('Table', () => {
|
|
|
114
123
|
// Rest of add column test remains the same as it's a more complex flow.
|
|
115
124
|
test('add column', async ({ browser, browserName }) => {
|
|
116
125
|
test.skip(browserName === 'webkit');
|
|
126
|
+
test.skip(browserName === 'firefox');
|
|
117
127
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
118
128
|
const table = new TableManager(page);
|
|
119
129
|
|
|
@@ -126,44 +136,9 @@ test.describe('Table', () => {
|
|
|
126
136
|
await page.close();
|
|
127
137
|
});
|
|
128
138
|
|
|
129
|
-
test('
|
|
130
|
-
test.skip(browserName === 'webkit');
|
|
131
|
-
const { page } = await setupPage(browser, { url: storyUrl });
|
|
132
|
-
const table = new TableManager(page);
|
|
133
|
-
|
|
134
|
-
await table.grid.ready();
|
|
135
|
-
|
|
136
|
-
// Reference existing object.
|
|
137
|
-
// Scroll over to the manager column.
|
|
138
|
-
await table.grid.panByWheel(10000, 0);
|
|
139
|
-
|
|
140
|
-
await table.grid.cell(4, 0, 'grid').click();
|
|
141
|
-
// TODO(wittjosiah): Surprisingly long delay needed here.
|
|
142
|
-
await page.keyboard.press('A', { delay: 1_000 });
|
|
143
|
-
await page.keyboard.press('u', { delay: 1_000 });
|
|
144
|
-
await page.getByRole('option', { name: 'Aut.' }).click();
|
|
145
|
-
|
|
146
|
-
// Assert that the value is shown in the cell.
|
|
147
|
-
await expect(page.getByRole('gridcell', { name: 'Aut.' }).first()).toBeVisible();
|
|
148
|
-
|
|
149
|
-
// Create new object.
|
|
150
|
-
await table.grid.cell(4, 1, 'grid').click();
|
|
151
|
-
// TODO(wittjosiah): Surprisingly long delay needed here.
|
|
152
|
-
await page.keyboard.type('t', { delay: 1_000 });
|
|
153
|
-
await page.keyboard.type('e', { delay: 1_000 });
|
|
154
|
-
await page.keyboard.type('s', { delay: 1_000 });
|
|
155
|
-
await page.keyboard.type('t', { delay: 1_000 });
|
|
156
|
-
await page.getByText('Create new object', { exact: false }).click();
|
|
157
|
-
await page.getByTestId('save-button').click();
|
|
158
|
-
|
|
159
|
-
// Scroll to the left.
|
|
160
|
-
await page.getByRole('gridcell', { name: 'test' }).first().click();
|
|
161
|
-
|
|
162
|
-
await page.close();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('test toggles', async ({ browser, browserName }) => {
|
|
139
|
+
test.skip('test toggles', async ({ browser, browserName }) => {
|
|
166
140
|
test.skip(browserName === 'webkit');
|
|
141
|
+
test.skip(browserName === 'firefox');
|
|
167
142
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
168
143
|
const table = new TableManager(page);
|
|
169
144
|
|
|
@@ -183,4 +158,121 @@ test.describe('Table', () => {
|
|
|
183
158
|
|
|
184
159
|
await page.close();
|
|
185
160
|
});
|
|
161
|
+
|
|
162
|
+
test('extant relations work as expected', async ({ browser, browserName }) => {
|
|
163
|
+
test.skip(browserName === 'webkit');
|
|
164
|
+
test.skip(browserName === 'firefox');
|
|
165
|
+
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
166
|
+
|
|
167
|
+
// Wait for the page to load
|
|
168
|
+
await page.locator('dx-grid > .dx-grid').nth(1).waitFor({ state: 'visible' });
|
|
169
|
+
|
|
170
|
+
// Find the dx-grid element for the contactModel (second table)
|
|
171
|
+
// The contactModel is used in the second Table.Main component in the story
|
|
172
|
+
const dxGrid = page.locator('dx-grid').nth(1);
|
|
173
|
+
await dxGrid.waitFor({ state: 'visible' });
|
|
174
|
+
|
|
175
|
+
// Scroll to the last column (column 4)
|
|
176
|
+
await dxGrid.evaluate(async (dxGridElement: DxGrid) => {
|
|
177
|
+
dxGridElement.scrollToColumn(4);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Click on the cell at aria-rowindex=0 aria-colindex=4 to focus it
|
|
181
|
+
const targetCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="4"]');
|
|
182
|
+
await targetCell.click();
|
|
183
|
+
|
|
184
|
+
// Click again to engage edit mode
|
|
185
|
+
await page.keyboard.press('Enter');
|
|
186
|
+
|
|
187
|
+
// Click on the combobox to open options
|
|
188
|
+
await page.getByRole('combobox').click();
|
|
189
|
+
await page.pause();
|
|
190
|
+
await page.getByPlaceholder('Search...').focus();
|
|
191
|
+
|
|
192
|
+
// Type the first few letters of an org name.
|
|
193
|
+
const orgName =
|
|
194
|
+
(await page
|
|
195
|
+
.locator('dx-grid')
|
|
196
|
+
.nth(0)
|
|
197
|
+
.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="0"] .dx-grid__cell__content')
|
|
198
|
+
.textContent()) ?? 'never';
|
|
199
|
+
await page.keyboard.type(orgName.substring(0, 4), { delay: 500 });
|
|
200
|
+
|
|
201
|
+
// Assert that there is an element with aria-selected on the page
|
|
202
|
+
await expect(page.locator('[role="option"][aria-selected]')).toBeVisible();
|
|
203
|
+
|
|
204
|
+
// Type the enter key
|
|
205
|
+
await page.keyboard.press('Enter');
|
|
206
|
+
|
|
207
|
+
// Save the result
|
|
208
|
+
await page.getByTestId('save-button').click();
|
|
209
|
+
|
|
210
|
+
// Assert that the cell element has the org name
|
|
211
|
+
await expect(targetCell).toHaveText(orgName);
|
|
212
|
+
|
|
213
|
+
await page.close();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('new relations work as expected', async ({ browser, browserName }) => {
|
|
217
|
+
test.skip(browserName === 'webkit');
|
|
218
|
+
test.skip(browserName === 'firefox');
|
|
219
|
+
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
220
|
+
|
|
221
|
+
// Wait for the page to load
|
|
222
|
+
await page.locator('dx-grid > .dx-grid').nth(1).waitFor({ state: 'visible' });
|
|
223
|
+
|
|
224
|
+
// Find the dx-grid element for the contactModel (second table)
|
|
225
|
+
// The contactModel is used in the second Table.Main component in the story
|
|
226
|
+
const dxGrid = page.locator('dx-grid').nth(1);
|
|
227
|
+
await dxGrid.waitFor({ state: 'visible' });
|
|
228
|
+
|
|
229
|
+
// Scroll to the last column (column 4)
|
|
230
|
+
await dxGrid.evaluate(async (dxGridElement: DxGrid) => {
|
|
231
|
+
dxGridElement.scrollToColumn(4);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Click on the cell at aria-rowindex=0 aria-colindex=8 to focus it
|
|
235
|
+
const targetCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="4"]');
|
|
236
|
+
await targetCell.click();
|
|
237
|
+
|
|
238
|
+
// Click again to engage edit mode
|
|
239
|
+
await page.keyboard.press('Enter');
|
|
240
|
+
|
|
241
|
+
// Click on the combobox to open options
|
|
242
|
+
await page.getByRole('combobox').click();
|
|
243
|
+
await page.getByPlaceholder('Search...').focus();
|
|
244
|
+
|
|
245
|
+
// Type the first few letters of an org name.
|
|
246
|
+
const orgName = 'Sally';
|
|
247
|
+
await page.keyboard.type(orgName, { delay: 500 });
|
|
248
|
+
|
|
249
|
+
// Assert that there is an element with aria-selected on the page
|
|
250
|
+
await expect(page.locator('[role="option"][aria-selected]')).toBeVisible();
|
|
251
|
+
|
|
252
|
+
// Type the enter key
|
|
253
|
+
await page.keyboard.press('Enter');
|
|
254
|
+
await page.pause();
|
|
255
|
+
|
|
256
|
+
// Click the save button in the popover
|
|
257
|
+
await page.getByTestId('save-button').click();
|
|
258
|
+
|
|
259
|
+
// Assert that the cell element has the org name
|
|
260
|
+
await expect(targetCell).toHaveText(orgName);
|
|
261
|
+
|
|
262
|
+
// Make a change to a non-ref cell to check that populated refs don’t cause problems with snapshots or schemas.
|
|
263
|
+
await dxGrid.evaluate(async (dxGridElement: DxGrid) => {
|
|
264
|
+
dxGridElement.scrollToColumn(6);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const nonRefContent = faker.lorem.words(3);
|
|
268
|
+
const nonRefCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="6"]');
|
|
269
|
+
await nonRefCell.click();
|
|
270
|
+
await page.keyboard.press('Enter');
|
|
271
|
+
await page.getByTestId('grid.cell-editor').waitFor({ state: 'visible' });
|
|
272
|
+
await page.keyboard.type(nonRefContent, { delay: 500 });
|
|
273
|
+
await page.keyboard.press('Enter');
|
|
274
|
+
await expect(nonRefCell).toHaveText(nonRefContent);
|
|
275
|
+
|
|
276
|
+
await page.close();
|
|
277
|
+
});
|
|
186
278
|
});
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import { useCallback, useMemo, useRef } from 'react';
|
|
6
6
|
|
|
7
|
-
import { isMutable, toJsonSchema } from '@dxos/echo
|
|
7
|
+
import { isMutable, toJsonSchema } from '@dxos/echo/internal';
|
|
8
8
|
import { useGlobalFilteredObjects } from '@dxos/plugin-search';
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
import { useClient } from '@dxos/react-client';
|
|
11
11
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
12
|
import { useClientProvider } from '@dxos/react-client/testing';
|
|
13
|
-
import { DataType, ProjectionModel } from '@dxos/schema';
|
|
13
|
+
import { DataType, ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
|
|
14
14
|
|
|
15
15
|
import { type TableController } from '../components';
|
|
16
|
-
import {
|
|
16
|
+
import { useAddRow, useTableModel } from '../hooks';
|
|
17
17
|
import { TablePresentation } from '../model';
|
|
18
18
|
|
|
19
19
|
faker.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
|
|
@@ -28,12 +28,15 @@ export const useTestTableModel = () => {
|
|
|
28
28
|
|
|
29
29
|
const views = useQuery(space, Filter.type(DataType.View));
|
|
30
30
|
const view = useMemo(() => views.at(0), [views]);
|
|
31
|
-
const
|
|
31
|
+
const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
|
|
32
|
+
const schema = useSchema(client, space, typename);
|
|
32
33
|
const jsonSchema = useMemo(() => (schema ? toJsonSchema(schema) : undefined), [schema]);
|
|
33
34
|
|
|
34
35
|
const projection = useMemo(() => {
|
|
35
36
|
if (schema && view?.projection) {
|
|
36
|
-
|
|
37
|
+
const projection = new ProjectionModel(toJsonSchema(schema), view.projection);
|
|
38
|
+
projection.normalizeView();
|
|
39
|
+
return projection;
|
|
37
40
|
}
|
|
38
41
|
}, [schema, view?.projection]);
|
|
39
42
|
|
|
@@ -92,8 +95,9 @@ export const useTestTableModel = () => {
|
|
|
92
95
|
});
|
|
93
96
|
|
|
94
97
|
const handleInsertRow = useCallback(() => {
|
|
95
|
-
model?.insertRow();
|
|
96
|
-
|
|
98
|
+
const insertResult = model?.insertRow();
|
|
99
|
+
tableRef.current?.handleInsertRowResult?.(insertResult);
|
|
100
|
+
}, [model, tableRef.current]);
|
|
97
101
|
|
|
98
102
|
const handleSaveView = useCallback(() => {
|
|
99
103
|
model?.saveView();
|
package/src/translations.ts
CHANGED
|
@@ -14,8 +14,8 @@ export const translations = [
|
|
|
14
14
|
'create object label': 'Create table',
|
|
15
15
|
'table name placeholder': 'Table name',
|
|
16
16
|
'settings title': 'Table settings',
|
|
17
|
-
'table schema label': '
|
|
18
|
-
'new schema': 'New
|
|
17
|
+
'table schema label': 'Record type',
|
|
18
|
+
'new schema': 'New record type',
|
|
19
19
|
'continue label': 'Continue',
|
|
20
20
|
'create stack section label': 'Create table',
|
|
21
21
|
'add row label': 'Add row',
|
package/src/types/index.ts
CHANGED
package/src/types/schema.ts
CHANGED
|
@@ -2,15 +2,72 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Match from 'effect/Match';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
|
-
import { Type } from '@dxos/echo';
|
|
8
|
-
import { JsonPath } from '@dxos/
|
|
8
|
+
import { Obj, Type } from '@dxos/echo';
|
|
9
|
+
import { JsonPath, type JsonSchemaType, toEffectSchema } from '@dxos/echo/internal';
|
|
10
|
+
import { ViewAnnotation } from '@dxos/echo/internal';
|
|
11
|
+
import { type SimpleType } from '@dxos/effect';
|
|
12
|
+
import { type CreateViewFromSpaceProps, type DataType, createViewFromSpace, getSchemaProperties } from '@dxos/schema';
|
|
9
13
|
|
|
10
|
-
export const
|
|
14
|
+
export const Table = Schema.Struct({
|
|
11
15
|
sizes: Schema.Record({
|
|
12
16
|
key: JsonPath,
|
|
13
17
|
value: Schema.Number,
|
|
14
18
|
}).pipe(Schema.mutable),
|
|
15
|
-
}).pipe(
|
|
16
|
-
|
|
19
|
+
}).pipe(
|
|
20
|
+
Type.Obj({
|
|
21
|
+
typename: 'dxos.org/type/Table',
|
|
22
|
+
version: '0.1.0',
|
|
23
|
+
}),
|
|
24
|
+
ViewAnnotation.set(true),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export type Table = Schema.Schema.Type<typeof Table>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Make a table view.
|
|
31
|
+
*/
|
|
32
|
+
export const make = (props: Partial<Obj.MakeProps<typeof Table>> = {}) => Obj.make(Table, { sizes: {}, ...props });
|
|
33
|
+
|
|
34
|
+
type MakeViewProps = Omit<CreateViewFromSpaceProps, 'presentation'> & {
|
|
35
|
+
sizes?: Record<string, number>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const makeView = async ({
|
|
39
|
+
sizes,
|
|
40
|
+
...props
|
|
41
|
+
}: MakeViewProps): Promise<{
|
|
42
|
+
jsonSchema: JsonSchemaType;
|
|
43
|
+
view: DataType.View;
|
|
44
|
+
schema: ReturnType<typeof toEffectSchema>;
|
|
45
|
+
}> => {
|
|
46
|
+
const table = Obj.make(Table, { sizes: {} });
|
|
47
|
+
const { jsonSchema, view } = await createViewFromSpace({ ...props, presentation: table });
|
|
48
|
+
|
|
49
|
+
// Preset sizes.
|
|
50
|
+
const schema = toEffectSchema(jsonSchema);
|
|
51
|
+
const shouldIncludeId = props.fields?.find((field) => field === 'id') !== undefined;
|
|
52
|
+
const properties = getSchemaProperties(schema.ast, {}, shouldIncludeId);
|
|
53
|
+
for (const property of properties) {
|
|
54
|
+
if (sizes?.[property.name]) {
|
|
55
|
+
table.sizes[property.name as JsonPath] = sizes[property.name];
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Match.type<SimpleType>().pipe(
|
|
60
|
+
Match.when('boolean', () => {
|
|
61
|
+
table.sizes[property.name as JsonPath] = 100;
|
|
62
|
+
}),
|
|
63
|
+
Match.when('number', () => {
|
|
64
|
+
table.sizes[property.name as JsonPath] = 100;
|
|
65
|
+
}),
|
|
66
|
+
Match.orElse(() => {
|
|
67
|
+
// Noop.
|
|
68
|
+
}),
|
|
69
|
+
)(property.type);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { jsonSchema, schema, view };
|
|
73
|
+
};
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import { Obj } from '@dxos/echo';
|
|
8
|
-
import { getTypename, toJsonSchema } from '@dxos/echo
|
|
9
|
-
import type { JsonSchemaType
|
|
7
|
+
import { Filter, Obj, Query } from '@dxos/echo';
|
|
8
|
+
import { getTypename, toJsonSchema } from '@dxos/echo/internal';
|
|
9
|
+
import type { JsonSchemaType } from '@dxos/echo/internal';
|
|
10
10
|
import {
|
|
11
|
-
createView,
|
|
12
11
|
type DataType,
|
|
13
|
-
getSchemaFromPropertyDefinitions,
|
|
14
12
|
ProjectionModel,
|
|
15
13
|
type SchemaPropertyDefinition,
|
|
14
|
+
type SortDirectionType,
|
|
15
|
+
createView,
|
|
16
|
+
getSchemaFromPropertyDefinitions,
|
|
16
17
|
} from '@dxos/schema';
|
|
17
18
|
|
|
18
|
-
import {
|
|
19
|
+
import { Table } from '../types';
|
|
19
20
|
|
|
20
21
|
// TODO(ZaymonFC): Upstream these extra fields to SchemaPropertyDefinition to enhance schema-tools schema creation.
|
|
21
22
|
type PropertyDisplayProps = {
|
|
@@ -54,23 +55,22 @@ export const getBaseSchema = ({
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export const makeDynamicTable = ({
|
|
57
|
-
typename,
|
|
58
58
|
jsonSchema,
|
|
59
59
|
properties,
|
|
60
60
|
}: {
|
|
61
|
-
typename: string;
|
|
62
61
|
jsonSchema: JsonSchemaType;
|
|
63
62
|
properties?: TablePropertyDefinition[];
|
|
64
63
|
}): { projection: ProjectionModel; view: DataType.View } => {
|
|
65
|
-
const table = Obj.make(
|
|
64
|
+
const table = Obj.make(Table.Table, { sizes: {} });
|
|
66
65
|
const view = createView({
|
|
67
|
-
|
|
66
|
+
query: Query.select(Filter.everything()),
|
|
68
67
|
jsonSchema,
|
|
69
68
|
presentation: table,
|
|
70
69
|
...(properties && { fields: properties.map((property) => property.name) }),
|
|
71
70
|
});
|
|
72
71
|
|
|
73
72
|
const projection = new ProjectionModel(jsonSchema, view.projection);
|
|
73
|
+
projection.normalizeView();
|
|
74
74
|
if (properties && projection.fields) {
|
|
75
75
|
setProperties(view, projection, table, properties);
|
|
76
76
|
}
|
|
@@ -81,7 +81,7 @@ export const makeDynamicTable = ({
|
|
|
81
81
|
const setProperties = (
|
|
82
82
|
view: DataType.View,
|
|
83
83
|
projection: ProjectionModel,
|
|
84
|
-
table:
|
|
84
|
+
table: Table.Table,
|
|
85
85
|
properties: TablePropertyDefinition[],
|
|
86
86
|
) => {
|
|
87
87
|
for (const property of properties) {
|
|
@@ -101,7 +101,7 @@ const setProperties = (
|
|
|
101
101
|
|
|
102
102
|
if (property.sort) {
|
|
103
103
|
const fieldId = field.id;
|
|
104
|
-
view.
|
|
104
|
+
view.sort = [{ fieldId, direction: property.sort }];
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
package/src/util/index.ts
CHANGED
package/src/util/schema.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a narrowed schema from an original schema that only includes
|
|
@@ -20,14 +20,14 @@ export const narrowSchema = <S extends Schema.Schema.AnyNoContext>(
|
|
|
20
20
|
): Schema.Schema<unknown, unknown> | undefined => {
|
|
21
21
|
const ast = (schema as any)?.ast;
|
|
22
22
|
|
|
23
|
-
if (isTypeLiteral(ast)) {
|
|
23
|
+
if (SchemaAST.isTypeLiteral(ast)) {
|
|
24
24
|
// Filter property signatures that match any of the provided paths
|
|
25
25
|
const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
|
|
26
26
|
|
|
27
27
|
// If we found at least one matching property
|
|
28
28
|
if (propertySignatures.length > 0) {
|
|
29
29
|
// Create a new TypeLiteral with only the matching properties
|
|
30
|
-
const narrowType = new TypeLiteral(propertySignatures, []);
|
|
30
|
+
const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
|
|
31
31
|
return Schema.make(narrowType);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -26,7 +26,7 @@ const createButton = ({
|
|
|
26
26
|
disabled = false,
|
|
27
27
|
icon,
|
|
28
28
|
testId,
|
|
29
|
-
type = 'primary',
|
|
29
|
+
type: _type = 'primary',
|
|
30
30
|
}: {
|
|
31
31
|
attr: string;
|
|
32
32
|
data: Record<string, string>;
|
|
@@ -39,8 +39,7 @@ const createButton = ({
|
|
|
39
39
|
.map(([k, v]) => `${k}="${v}"`)
|
|
40
40
|
.join(' ');
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
return `<button ${attr} data-testid="${testId}" class="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? 'disabled' : ''}><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
|
|
42
|
+
return `<button ${attr} data-testid="${testId}" class="dx-button is-6 aspect-square pli-0.5 min-bs-0" ${dataAttrs} ${disabled ? 'disabled' : ''} data-dx-grid-action="accessory"><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
const addColumnButton = {
|
|
@@ -67,6 +66,7 @@ const columnSettingsButton = {
|
|
|
67
66
|
icon: columnSettingsButton.icon,
|
|
68
67
|
data: {
|
|
69
68
|
'data-field-id': fieldId,
|
|
69
|
+
'data-variant': 'ghost',
|
|
70
70
|
},
|
|
71
71
|
testId: 'table-column-settings-button',
|
|
72
72
|
});
|
|
@@ -130,6 +130,7 @@ const saveDraftRowButton = {
|
|
|
130
130
|
disabled,
|
|
131
131
|
data: {
|
|
132
132
|
'data-row-index': rowIndex.toString(),
|
|
133
|
+
'data-variant': 'primary',
|
|
133
134
|
},
|
|
134
135
|
testId: 'table-save-draft-row-button',
|
|
135
136
|
});
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
// Core types.
|
|
6
|
+
import { accessoryHandlesPointerdownAttrs } from '@dxos/lit-grid';
|
|
7
|
+
|
|
6
8
|
export type TableControl = 'checkbox' | 'switch';
|
|
7
9
|
|
|
8
10
|
export type ControlData =
|
|
@@ -41,7 +43,7 @@ const renderInput = (
|
|
|
41
43
|
preventToggle = false,
|
|
42
44
|
testId: string,
|
|
43
45
|
) => {
|
|
44
|
-
return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? 'checked' : ''} ${preventToggle ? 'onclick="return false"' : ''} ${disabled ? 'disabled' : ''} data-testid="${testId}"/>`;
|
|
46
|
+
return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? 'checked' : ''} ${preventToggle ? 'onclick="return false"' : ''} ${disabled ? 'disabled' : ''} data-testid="${testId}" data-dx-grid-action="accessory"/>`;
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
export const CheckboxStory = ({
|
|
@@ -64,6 +66,7 @@ export const SwitchStory = ({ colIndex, rowIndex, checked = false, disabled = fa
|
|
|
64
66
|
[CONTROL_IDENTIFIERS.switch]: '',
|
|
65
67
|
'data-row-index': rowIndex.toString(),
|
|
66
68
|
'data-col-index': colIndex.toString(),
|
|
69
|
+
...accessoryHandlesPointerdownAttrs,
|
|
67
70
|
};
|
|
68
71
|
|
|
69
72
|
return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, 'table-switch');
|