@dxos/react-ui-table 0.8.3 → 0.8.4-main.1da679c
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-63UUF4IG.mjs +66 -0
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +7 -0
- package/dist/lib/browser/chunk-BBXDTWCX.mjs +1822 -0
- package/dist/lib/browser/chunk-BBXDTWCX.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +816 -2152
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +124 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-K734DJB7.mjs +67 -0
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs +1823 -0
- package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +816 -2152
- 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 +125 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- 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 +4 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +50 -2
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +44 -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 +3 -2
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +42 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +8 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +9 -15
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -3
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.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 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +14 -0
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -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 +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +59 -21
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +10 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +3 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +22 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +5 -6
- package/dist/types/src/translations.d.ts.map +1 -1
- 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 +28 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- 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/table-buttons.d.ts +15 -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 +57 -44
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +30 -11
- package/src/components/Table/DynamicTable.stories.tsx +8 -5
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +56 -47
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +126 -159
- package/src/components/Table/Table.tsx +169 -123
- package/src/components/TableCellEditor/CellValidationMessage.tsx +7 -2
- package/src/components/TableCellEditor/FormCellEditor.tsx +158 -48
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +31 -47
- package/src/components/TableCellEditor/TableCellEditor.tsx +106 -111
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddRow.ts +38 -0
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +1 -2
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +12 -9
- package/src/model/table-model.ts +288 -88
- package/src/model/table-presentation.test.ts +12 -8
- package/src/model/table-presentation.ts +263 -138
- package/src/model/table-sorting.ts +22 -13
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +137 -44
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +124 -0
- package/src/translations.ts +7 -5
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +73 -0
- package/src/util/dynamic-table.ts +24 -21
- package/src/util/index.ts +0 -1
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +28 -4
- package/src/util/table-controls.ts +4 -1
- package/dist/lib/browser/chunk-TL7CKQQV.mjs +0 -27
- package/dist/lib/browser/chunk-TL7CKQQV.mjs.map +0 -7
- package/dist/lib/node/chunk-O2VPBNS5.cjs +0 -50
- package/dist/lib/node/chunk-O2VPBNS5.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -2739
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/types/index.cjs +0 -31
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs +0 -29
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs.map +0 -7
- 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/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/dist/types/src/util/table.d.ts +0 -14
- package/dist/types/src/util/table.d.ts.map +0 -1
- package/src/components/TableCellEditor/extension.ts +0 -61
- package/src/playwright/playwright.config.cts +0 -18
- package/src/types/table.ts +0 -27
- package/src/util/table.ts +0 -127
|
@@ -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
|
-
const storyUrl = storybookUrl('ui-react-ui-table-table--default');
|
|
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,22 +27,24 @@ 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
|
|
|
29
34
|
await table.grid.ready();
|
|
30
35
|
await table.sortColumn(0, 'descending');
|
|
31
|
-
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('
|
|
32
|
-
await expect(table.grid.cell(0, 9, 'grid')).toContainText('
|
|
36
|
+
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('Sit.');
|
|
37
|
+
await expect(table.grid.cell(0, 9, 'grid')).toContainText('Aut.');
|
|
33
38
|
|
|
34
39
|
await table.sortColumn(0, 'ascending');
|
|
35
|
-
await expect(table.grid.cell(0, 0, 'grid')).toContainText('
|
|
36
|
-
await expect(table.grid.cell(0, 9, 'grid')).toHaveText('
|
|
40
|
+
await expect(table.grid.cell(0, 0, 'grid')).toContainText('Aut.');
|
|
41
|
+
await expect(table.grid.cell(0, 9, 'grid')).toHaveText('Sit.');
|
|
37
42
|
await page.close();
|
|
38
43
|
});
|
|
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,17 +66,19 @@ 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
|
|
|
66
73
|
await table.grid.ready();
|
|
67
74
|
await table.deleteRow(0);
|
|
68
|
-
await expect(page.getByRole('gridcell', { name: '
|
|
75
|
+
await expect(page.getByRole('gridcell', { name: 'Sapiente.' })).toHaveCount(0);
|
|
69
76
|
await page.close();
|
|
70
77
|
});
|
|
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
|
|
|
@@ -80,14 +88,15 @@ test.describe('Table', () => {
|
|
|
80
88
|
// Delete action affects all selected rows.
|
|
81
89
|
await table.deleteRow(0);
|
|
82
90
|
|
|
83
|
-
await expect(page.getByRole('gridcell', { name: '
|
|
84
|
-
await expect(page.getByRole('gridcell', { name: '
|
|
91
|
+
await expect(page.getByRole('gridcell', { name: 'Sapiente.' })).toHaveCount(0);
|
|
92
|
+
await expect(page.getByRole('gridcell', { name: 'Beatae.' })).toHaveCount(0);
|
|
85
93
|
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(0);
|
|
86
94
|
await page.close();
|
|
87
95
|
});
|
|
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
|
|
|
@@ -112,9 +121,9 @@ test.describe('Table', () => {
|
|
|
112
121
|
});
|
|
113
122
|
|
|
114
123
|
// Rest of add column test remains the same as it's a more complex flow.
|
|
115
|
-
// TODO(ZaymonFC): Restore this after fixing format selection.
|
|
116
124
|
test('add column', async ({ browser, browserName }) => {
|
|
117
125
|
test.skip(browserName === 'webkit');
|
|
126
|
+
test.skip(browserName === 'firefox');
|
|
118
127
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
119
128
|
const table = new TableManager(page);
|
|
120
129
|
|
|
@@ -127,58 +136,142 @@ test.describe('Table', () => {
|
|
|
127
136
|
await page.close();
|
|
128
137
|
});
|
|
129
138
|
|
|
130
|
-
test('
|
|
139
|
+
test.skip('test toggles', async ({ browser, browserName }) => {
|
|
131
140
|
test.skip(browserName === 'webkit');
|
|
141
|
+
test.skip(browserName === 'firefox');
|
|
132
142
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
133
143
|
const table = new TableManager(page);
|
|
134
144
|
|
|
135
145
|
await table.grid.ready();
|
|
136
146
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
await table.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await page.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await expect(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
147
|
+
await page.getByTestId('table-switch').first().click();
|
|
148
|
+
await page.getByTestId('table-switch').nth(7).click();
|
|
149
|
+
|
|
150
|
+
// Test that checks are durable in the data model by sorting.
|
|
151
|
+
await table.sortColumn(1, 'descending');
|
|
152
|
+
|
|
153
|
+
// Assert the first two switch checkboxes are checked.
|
|
154
|
+
await expect(page.getByTestId('table-switch').first()).toBeChecked();
|
|
155
|
+
await expect(page.getByTestId('table-switch').nth(1)).toBeChecked();
|
|
156
|
+
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('Sapiente.');
|
|
157
|
+
await expect(table.grid.cell(0, 1, 'grid')).toHaveText('Beatae.');
|
|
158
|
+
|
|
159
|
+
await page.close();
|
|
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
|
|
156
208
|
await page.getByTestId('save-button').click();
|
|
157
209
|
|
|
158
|
-
//
|
|
159
|
-
await
|
|
210
|
+
// Assert that the cell element has the org name
|
|
211
|
+
await expect(targetCell).toHaveText(orgName);
|
|
160
212
|
|
|
161
213
|
await page.close();
|
|
162
214
|
});
|
|
163
215
|
|
|
164
|
-
test('
|
|
216
|
+
test('new relations work as expected', async ({ browser, browserName }) => {
|
|
165
217
|
test.skip(browserName === 'webkit');
|
|
166
|
-
|
|
167
|
-
const
|
|
218
|
+
test.skip(browserName === 'firefox');
|
|
219
|
+
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
168
220
|
|
|
169
|
-
|
|
221
|
+
// Wait for the page to load
|
|
222
|
+
await page.locator('dx-grid > .dx-grid').nth(1).waitFor({ state: 'visible' });
|
|
170
223
|
|
|
171
|
-
|
|
172
|
-
|
|
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' });
|
|
173
228
|
|
|
174
|
-
//
|
|
175
|
-
await
|
|
229
|
+
// Scroll to the last column (column 4)
|
|
230
|
+
await dxGrid.evaluate(async (dxGridElement: DxGrid) => {
|
|
231
|
+
dxGridElement.scrollToColumn(4);
|
|
232
|
+
});
|
|
176
233
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
await
|
|
180
|
-
|
|
181
|
-
|
|
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);
|
|
182
275
|
|
|
183
276
|
await page.close();
|
|
184
277
|
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { isMutable, toJsonSchema } from '@dxos/echo-schema';
|
|
8
|
+
import { useGlobalFilteredObjects } from '@dxos/plugin-search';
|
|
9
|
+
import { faker } from '@dxos/random';
|
|
10
|
+
import { useClient } from '@dxos/react-client';
|
|
11
|
+
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
|
+
import { useClientProvider } from '@dxos/react-client/testing';
|
|
13
|
+
import { DataType, ProjectionModel, typenameFromQuery } from '@dxos/schema';
|
|
14
|
+
|
|
15
|
+
import { type TableController } from '../components';
|
|
16
|
+
import { useAddRow, useTableModel } from '../hooks';
|
|
17
|
+
import { TablePresentation } from '../model';
|
|
18
|
+
|
|
19
|
+
faker.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Custom hook to create and manage a test table model for storybook demonstrations.
|
|
23
|
+
* Provides table data, schema, and handlers for table operations.
|
|
24
|
+
*/
|
|
25
|
+
export const useTestTableModel = () => {
|
|
26
|
+
const client = useClient();
|
|
27
|
+
const { space } = useClientProvider();
|
|
28
|
+
|
|
29
|
+
const views = useQuery(space, Filter.type(DataType.View));
|
|
30
|
+
const view = useMemo(() => views.at(0), [views]);
|
|
31
|
+
const typename = view?.query ? typenameFromQuery(view.query) : undefined;
|
|
32
|
+
const schema = useSchema(client, space, typename);
|
|
33
|
+
const jsonSchema = useMemo(() => (schema ? toJsonSchema(schema) : undefined), [schema]);
|
|
34
|
+
|
|
35
|
+
const projection = useMemo(() => {
|
|
36
|
+
if (schema && view?.projection) {
|
|
37
|
+
return new ProjectionModel(toJsonSchema(schema), view.projection);
|
|
38
|
+
}
|
|
39
|
+
}, [schema, view?.projection]);
|
|
40
|
+
|
|
41
|
+
const features = useMemo(
|
|
42
|
+
() => ({
|
|
43
|
+
selection: { enabled: true, mode: 'multiple' as const },
|
|
44
|
+
dataEditable: true,
|
|
45
|
+
schemaEditable: schema && isMutable(schema),
|
|
46
|
+
}),
|
|
47
|
+
[schema],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
|
|
51
|
+
const filteredObjects = useGlobalFilteredObjects(objects);
|
|
52
|
+
|
|
53
|
+
const tableRef = useRef<TableController>(null);
|
|
54
|
+
const handleCellUpdate = useCallback((cell: any) => {
|
|
55
|
+
tableRef.current?.update?.(cell);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const handleRowOrderChange = useCallback(() => {
|
|
59
|
+
tableRef.current?.update?.();
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const addRow = useAddRow({ space, schema });
|
|
63
|
+
|
|
64
|
+
const handleDeleteRows = useCallback(
|
|
65
|
+
(_: number, objects: any[]) => {
|
|
66
|
+
for (const object of objects) {
|
|
67
|
+
space?.db.remove(object);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[space],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const handleDeleteColumn = useCallback(
|
|
74
|
+
(fieldId: string) => {
|
|
75
|
+
if (projection) {
|
|
76
|
+
projection.deleteFieldProjection(fieldId);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[projection],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const model = useTableModel({
|
|
83
|
+
view,
|
|
84
|
+
schema: jsonSchema,
|
|
85
|
+
projection,
|
|
86
|
+
features,
|
|
87
|
+
rows: filteredObjects,
|
|
88
|
+
onInsertRow: addRow,
|
|
89
|
+
onDeleteRows: handleDeleteRows,
|
|
90
|
+
onDeleteColumn: handleDeleteColumn,
|
|
91
|
+
onCellUpdate: handleCellUpdate,
|
|
92
|
+
onRowOrderChange: handleRowOrderChange,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const handleInsertRow = useCallback(() => {
|
|
96
|
+
const insertResult = model?.insertRow();
|
|
97
|
+
tableRef.current?.handleInsertRowResult?.(insertResult);
|
|
98
|
+
}, [model, tableRef.current]);
|
|
99
|
+
|
|
100
|
+
const handleSaveView = useCallback(() => {
|
|
101
|
+
model?.saveView();
|
|
102
|
+
}, [model]);
|
|
103
|
+
|
|
104
|
+
const presentation = useMemo(() => {
|
|
105
|
+
if (model) {
|
|
106
|
+
return new TablePresentation(model);
|
|
107
|
+
}
|
|
108
|
+
}, [model]);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
schema,
|
|
112
|
+
view,
|
|
113
|
+
projection,
|
|
114
|
+
tableRef,
|
|
115
|
+
model,
|
|
116
|
+
presentation,
|
|
117
|
+
space,
|
|
118
|
+
client,
|
|
119
|
+
handleInsertRow,
|
|
120
|
+
handleSaveView,
|
|
121
|
+
handleDeleteRows,
|
|
122
|
+
handleDeleteColumn,
|
|
123
|
+
};
|
|
124
|
+
};
|
package/src/translations.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { type Resource } from '@dxos/react-ui';
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export const translationKey = 'react-ui-table';
|
|
8
|
+
|
|
9
|
+
export const translations = [
|
|
8
10
|
{
|
|
9
11
|
'en-US': {
|
|
10
12
|
[translationKey]: {
|
|
@@ -12,8 +14,8 @@ export default [
|
|
|
12
14
|
'create object label': 'Create table',
|
|
13
15
|
'table name placeholder': 'Table name',
|
|
14
16
|
'settings title': 'Table settings',
|
|
15
|
-
'table schema label': '
|
|
16
|
-
'new schema': 'New
|
|
17
|
+
'table schema label': 'Record type',
|
|
18
|
+
'new schema': 'New record type',
|
|
17
19
|
'continue label': 'Continue',
|
|
18
20
|
'create stack section label': 'Create table',
|
|
19
21
|
'add row label': 'Add row',
|
|
@@ -32,4 +34,4 @@ export default [
|
|
|
32
34
|
},
|
|
33
35
|
},
|
|
34
36
|
},
|
|
35
|
-
] as const;
|
|
37
|
+
] as const satisfies Resource[];
|
package/src/types/index.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
|
+
import { Match } from 'effect';
|
|
7
|
+
|
|
8
|
+
import { Obj, Type } from '@dxos/echo';
|
|
9
|
+
import { JsonPath, type JsonSchemaType, toEffectSchema } from '@dxos/echo-schema';
|
|
10
|
+
import { ViewAnnotation } from '@dxos/echo-schema';
|
|
11
|
+
import { type SimpleType } from '@dxos/effect';
|
|
12
|
+
import { type CreateViewFromSpaceProps, type DataType, createViewFromSpace, getSchemaProperties } from '@dxos/schema';
|
|
13
|
+
|
|
14
|
+
export const Table = Schema.Struct({
|
|
15
|
+
sizes: Schema.Record({
|
|
16
|
+
key: JsonPath,
|
|
17
|
+
value: Schema.Number,
|
|
18
|
+
}).pipe(Schema.mutable),
|
|
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
|
+
};
|
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Schema } from 'effect';
|
|
6
6
|
|
|
7
|
-
import { Obj,
|
|
7
|
+
import { Filter, Obj, Query } from '@dxos/echo';
|
|
8
8
|
import { getTypename, toJsonSchema } from '@dxos/echo-schema';
|
|
9
|
-
import type { JsonSchemaType
|
|
9
|
+
import type { JsonSchemaType } from '@dxos/echo-schema';
|
|
10
10
|
import {
|
|
11
|
+
type DataType,
|
|
12
|
+
ProjectionModel,
|
|
13
|
+
type SchemaPropertyDefinition,
|
|
14
|
+
type SortDirectionType,
|
|
11
15
|
createView,
|
|
12
16
|
getSchemaFromPropertyDefinitions,
|
|
13
|
-
ViewProjection,
|
|
14
|
-
type ViewType,
|
|
15
|
-
type SchemaPropertyDefinition,
|
|
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 = {
|
|
@@ -61,32 +62,34 @@ export const makeDynamicTable = ({
|
|
|
61
62
|
typename: string;
|
|
62
63
|
jsonSchema: JsonSchemaType;
|
|
63
64
|
properties?: TablePropertyDefinition[];
|
|
64
|
-
}): {
|
|
65
|
+
}): { projection: ProjectionModel; view: DataType.View } => {
|
|
66
|
+
const table = Obj.make(Table.Table, { sizes: {} });
|
|
65
67
|
const view = createView({
|
|
66
|
-
|
|
67
|
-
typename,
|
|
68
|
+
query: Query.select(Filter.typename(typename)),
|
|
68
69
|
jsonSchema,
|
|
70
|
+
presentation: table,
|
|
69
71
|
...(properties && { fields: properties.map((property) => property.name) }),
|
|
70
72
|
});
|
|
71
73
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
setProperties(view, projection, properties);
|
|
74
|
+
const projection = new ProjectionModel(jsonSchema, view.projection);
|
|
75
|
+
if (properties && projection.fields) {
|
|
76
|
+
setProperties(view, projection, table, properties);
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
return {
|
|
79
|
-
table,
|
|
80
|
-
projection,
|
|
81
|
-
};
|
|
79
|
+
return { projection, view };
|
|
82
80
|
};
|
|
83
81
|
|
|
84
|
-
const setProperties = (
|
|
82
|
+
const setProperties = (
|
|
83
|
+
view: DataType.View,
|
|
84
|
+
projection: ProjectionModel,
|
|
85
|
+
table: Table.Table,
|
|
86
|
+
properties: TablePropertyDefinition[],
|
|
87
|
+
) => {
|
|
85
88
|
for (const property of properties) {
|
|
86
|
-
const field =
|
|
89
|
+
const field = projection.fields.find((field) => field.path === property.name);
|
|
87
90
|
if (field) {
|
|
88
91
|
if (property.size !== undefined) {
|
|
89
|
-
field.
|
|
92
|
+
table.sizes[field.path] = property.size;
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
if (property.title !== undefined) {
|
|
@@ -99,7 +102,7 @@ const setProperties = (view: ViewType, projection: ViewProjection, properties: T
|
|
|
99
102
|
|
|
100
103
|
if (property.sort) {
|
|
101
104
|
const fieldId = field.id;
|
|
102
|
-
view.
|
|
105
|
+
view.sort = [{ fieldId, direction: property.sort }];
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
}
|
package/src/util/index.ts
CHANGED
package/src/util/schema.ts
CHANGED