@dxos/react-ui-table 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29
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-FBURZBPH.mjs +70 -0
- 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-YGC7LZJN.mjs +1695 -0
- package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1139 -1193
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +33 -42
- 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 +4 -3
- package/dist/lib/node-esm/chunk-DKKMSVKP.mjs +71 -0
- package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs +1697 -0
- 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 +1139 -1193
- 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 +33 -42
- 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 +4 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +23 -39
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +96 -29
- 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 -18
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +96 -31
- 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 +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +7 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +25 -28
- 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/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +23 -16
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +9 -9
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -8
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +18 -9
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +106 -26
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +3 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +21 -23
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +26 -0
- package/dist/types/src/types/Table.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +14 -12
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/schema.d.ts +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 +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- 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 +67 -76
- package/src/components/Table/ColumnActionsMenu.tsx +17 -12
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +34 -33
- package/src/components/Table/DynamicTable.tsx +33 -36
- package/src/components/Table/Relations.stories.tsx +196 -48
- package/src/components/Table/RowActionsMenu.tsx +27 -17
- package/src/components/Table/Table.stories.tsx +144 -127
- package/src/components/Table/Table.tsx +43 -403
- package/src/components/Table/TableContent.tsx +421 -0
- package/src/components/Table/TableToolbar.tsx +111 -0
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
- package/src/components/TableCellEditor/FormCellEditor.tsx +125 -131
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +32 -55
- package/src/components/TableCellEditor/TableCellEditor.tsx +39 -124
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +44 -18
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/index.ts +0 -4
- package/src/model/modal-controller.ts +23 -40
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +93 -56
- package/src/model/table-model.ts +437 -170
- package/src/model/table-presentation.test.ts +67 -56
- package/src/model/table-presentation.ts +45 -41
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +59 -13
- package/src/testing/useTestTableModel.ts +48 -39
- package/src/translations.ts +20 -22
- package/src/types/Table.ts +79 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +43 -31
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/schema.ts +4 -4
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-63UUF4IG.mjs +0 -66
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
- package/dist/lib/browser/chunk-GPZSXPSA.mjs +0 -1822
- package/dist/lib/browser/chunk-GPZSXPSA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K734DJB7.mjs +0 -67
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XUD6W42C.mjs +0 -1823
- package/dist/lib/node-esm/chunk-XUD6W42C.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/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/dist/types/src/model/table-sorting.d.ts +0 -52
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -28
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/src/components/Table/CreateRefPanel.tsx +0 -103
- package/src/components/TableToolbar/TableToolbar.tsx +0 -102
- package/src/components/TableToolbar/index.ts +0 -5
- package/src/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
|
@@ -2,102 +2,113 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { createView } from '@dxos/schema';
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { Table } from '../types';
|
|
14
|
-
|
|
15
14
|
import { TableModel, type TableModelProps } from './table-model';
|
|
16
15
|
import { TablePresentation } from './table-presentation';
|
|
17
16
|
|
|
18
17
|
describe('TablePresentation', () => {
|
|
19
|
-
describe('
|
|
18
|
+
describe('getCells', () => {
|
|
19
|
+
let registry: Registry.Registry;
|
|
20
20
|
let data: any[];
|
|
21
|
-
let updateCount: number;
|
|
22
21
|
let model: any;
|
|
23
22
|
let presentation: TablePresentation;
|
|
24
23
|
|
|
25
24
|
beforeEach(async () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
model = createTableModel({
|
|
35
|
-
onCellUpdate: () => updateCount++,
|
|
36
|
-
});
|
|
37
|
-
model.setRows(data);
|
|
25
|
+
registry = Registry.make();
|
|
26
|
+
data = [
|
|
27
|
+
{ id: '1', title: 'A', count: 1 },
|
|
28
|
+
{ id: '2', title: 'B', count: 2 },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
model = createTableModel(registry, {});
|
|
38
32
|
await model.open();
|
|
39
|
-
|
|
33
|
+
model.setRows(data);
|
|
34
|
+
presentation = new TablePresentation(registry, model);
|
|
40
35
|
});
|
|
41
36
|
|
|
42
|
-
it('should
|
|
43
|
-
|
|
44
|
-
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
37
|
+
it('should return cells for the visible range', () => {
|
|
38
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
45
39
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
expect(
|
|
40
|
+
// Verify that cells are returned for the range. The exact column indices depend on the projection.
|
|
41
|
+
const cellKeys = Object.keys(cells);
|
|
42
|
+
expect(cellKeys.length).toBeGreaterThan(0);
|
|
49
43
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
expect(
|
|
44
|
+
// Check that values from the data are present in the cells.
|
|
45
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
46
|
+
expect(values).toContain('A');
|
|
47
|
+
expect(values).toContain('B');
|
|
53
48
|
});
|
|
54
49
|
|
|
55
|
-
it('should
|
|
56
|
-
//
|
|
57
|
-
|
|
50
|
+
it('should reflect row data changes', () => {
|
|
51
|
+
// Update the row data and refresh.
|
|
52
|
+
data[0].title = 'New Value';
|
|
53
|
+
model.setRows(data);
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
// Verify the new value through getCells.
|
|
56
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 2 } }, 'grid');
|
|
57
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
58
|
+
expect(values).toContain('New Value');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should reflect new rows', () => {
|
|
62
|
+
// Add a new row.
|
|
63
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
64
|
+
model.setRows(data);
|
|
61
65
|
|
|
62
66
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
63
67
|
|
|
64
68
|
// Verify the new row's data.
|
|
65
69
|
expect(cells['0,2']?.value).toBe('C');
|
|
66
70
|
expect(cells['1,2']?.value).toBe('3');
|
|
67
|
-
expect(cells['2,2']?.value).toBe('true');
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
it('should handle
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Multiple operations.
|
|
75
|
-
data[0].col1 = 'Updated A';
|
|
76
|
-
data.push({ col1: 'C', col2: 3, col3: true });
|
|
73
|
+
it('should handle row modifications', () => {
|
|
74
|
+
// Modify the data.
|
|
75
|
+
data[0].title = 'Updated A';
|
|
76
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
77
77
|
data.splice(1, 1);
|
|
78
|
-
|
|
79
|
-
// We expect one update per operation affecting visible rows.
|
|
80
|
-
expect(updateCount).toBe(3);
|
|
78
|
+
model.setRows(data);
|
|
81
79
|
|
|
82
80
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
83
81
|
|
|
84
82
|
// Verify final state.
|
|
85
83
|
expect(cells['0,0']?.value).toBe('Updated A');
|
|
86
84
|
expect(cells['0,1']?.value).toBe('C');
|
|
87
|
-
expect(cells['1,1']?.value).toBe('3');
|
|
88
|
-
expect(cells['2,1']?.value).toBe('true');
|
|
89
85
|
});
|
|
90
86
|
});
|
|
91
87
|
});
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
const Test = Schema.Struct({
|
|
94
90
|
title: Schema.String,
|
|
95
|
-
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
count: Schema.Number,
|
|
92
|
+
}).pipe(
|
|
93
|
+
Type.object({
|
|
94
|
+
typename: 'com.example.type.test',
|
|
95
|
+
version: '0.1.0',
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
99
100
|
const schema = createEchoSchema(Test);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
const view = ViewModel.make({
|
|
102
|
+
query: Query.select(Filter.type(schema)),
|
|
103
|
+
jsonSchema: schema.jsonSchema,
|
|
104
|
+
});
|
|
105
|
+
const object = Table.make({ view });
|
|
106
|
+
const projection = new ProjectionModel({
|
|
107
|
+
registry,
|
|
108
|
+
view,
|
|
109
|
+
baseSchema: schema.jsonSchema,
|
|
110
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
111
|
+
});
|
|
112
|
+
projection.normalizeView();
|
|
113
|
+
return new TableModel({ registry, object, projection, ...props });
|
|
103
114
|
};
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import {
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
10
|
+
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
11
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
12
|
import {
|
|
12
13
|
type DxGridCellValue,
|
|
@@ -15,11 +16,10 @@ import {
|
|
|
15
16
|
type DxGridPlaneRange,
|
|
16
17
|
toPlaneCellIndex,
|
|
17
18
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
19
|
+
import { VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
20
|
+
import { mx } from '@dxos/ui-theme';
|
|
20
21
|
|
|
21
22
|
import { tableButtons, tableControls } from '../util';
|
|
22
|
-
|
|
23
23
|
import { type SelectionMode } from './selection-model';
|
|
24
24
|
import { type TableModel, type TableRow } from './table-model';
|
|
25
25
|
|
|
@@ -29,15 +29,20 @@ import { type TableModel, type TableRow } from './table-model';
|
|
|
29
29
|
* different grid planes.
|
|
30
30
|
*/
|
|
31
31
|
export class TablePresentation<T extends TableRow = TableRow> {
|
|
32
|
+
private readonly _registry: Registry.Registry;
|
|
33
|
+
private readonly _visibleRange: Atom.Writable<DxGridPlaneRange>;
|
|
32
34
|
private fieldProjectionCache = new Map<string, ReturnType<typeof this.model.projection.getFieldProjection>>();
|
|
33
35
|
|
|
34
36
|
constructor(
|
|
37
|
+
registry: Registry.Registry,
|
|
35
38
|
private readonly model: TableModel<T>,
|
|
36
|
-
|
|
39
|
+
) {
|
|
40
|
+
this._registry = registry;
|
|
41
|
+
this._visibleRange = Atom.make<DxGridPlaneRange>({
|
|
37
42
|
start: { row: 0, col: 0 },
|
|
38
43
|
end: { row: 0, col: 0 },
|
|
39
|
-
})
|
|
40
|
-
|
|
44
|
+
});
|
|
45
|
+
}
|
|
41
46
|
|
|
42
47
|
public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
|
|
43
48
|
// Clear cache at the start of each render pass
|
|
@@ -47,7 +52,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
47
52
|
|
|
48
53
|
switch (plane) {
|
|
49
54
|
case 'grid':
|
|
50
|
-
this._visibleRange
|
|
55
|
+
this._registry.set(this._visibleRange, range);
|
|
51
56
|
cells = this.getMainGridCells(range);
|
|
52
57
|
break;
|
|
53
58
|
case 'frozenRowsStart':
|
|
@@ -102,7 +107,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
102
107
|
private createDataCell(
|
|
103
108
|
cells: DxGridPlaneCells,
|
|
104
109
|
obj: T,
|
|
105
|
-
field: FieldType,
|
|
110
|
+
field: View.FieldType,
|
|
106
111
|
colIndex: number,
|
|
107
112
|
displayIndex: number,
|
|
108
113
|
): void {
|
|
@@ -120,12 +125,12 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
switch (props.format) {
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
128
|
+
case Format.TypeFormat.Boolean:
|
|
129
|
+
case Format.TypeFormat.SingleSelect:
|
|
130
|
+
case Format.TypeFormat.MultiSelect: {
|
|
126
131
|
return '';
|
|
127
132
|
}
|
|
128
|
-
case
|
|
133
|
+
case Format.TypeFormat.Ref: {
|
|
129
134
|
if (!field.referencePath) {
|
|
130
135
|
return ''; // TODO(burdon): Show error.
|
|
131
136
|
}
|
|
@@ -190,7 +195,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
190
195
|
|
|
191
196
|
const tags = targetArray
|
|
192
197
|
.map(getLabel)
|
|
193
|
-
.filter(isNotNullable)
|
|
198
|
+
.filter(Predicate.isNotNullable)
|
|
194
199
|
.map((title) => {
|
|
195
200
|
return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
|
|
196
201
|
})
|
|
@@ -201,16 +206,16 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
// References.
|
|
204
|
-
if (props.format ===
|
|
209
|
+
if (props.format === Format.TypeFormat.Ref && props.referenceSchema) {
|
|
205
210
|
const targetObj = getValue(obj, field.path)?.target;
|
|
206
211
|
if (targetObj) {
|
|
207
212
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
208
|
-
cell.accessoryHtml = `<dx-anchor
|
|
213
|
+
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor dxn=${dxn} class="dx-button w-6 aspect-square min-h-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
209
214
|
}
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
// Booleans.
|
|
213
|
-
if (props.format ===
|
|
218
|
+
if (props.format === Format.TypeFormat.Boolean) {
|
|
214
219
|
const value = getValue(obj, field.path);
|
|
215
220
|
cell.accessoryHtml = tableControls.switch.render({
|
|
216
221
|
colIndex,
|
|
@@ -221,7 +226,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
221
226
|
}
|
|
222
227
|
|
|
223
228
|
// Single-Selects.
|
|
224
|
-
if (props.format ===
|
|
229
|
+
if (props.format === Format.TypeFormat.SingleSelect) {
|
|
225
230
|
const value = getValue(obj, field.path);
|
|
226
231
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
227
232
|
if (options) {
|
|
@@ -233,7 +238,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
233
238
|
}
|
|
234
239
|
|
|
235
240
|
// Multi-Selects.
|
|
236
|
-
if (props.format ===
|
|
241
|
+
if (props.format === Format.TypeFormat.MultiSelect) {
|
|
237
242
|
const values = getValue(obj, field.path) as string[] | undefined;
|
|
238
243
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
239
244
|
if (options && values && values.length > 0) {
|
|
@@ -260,7 +265,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
260
265
|
|
|
261
266
|
private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
262
267
|
const cells: DxGridPlaneCells = {};
|
|
263
|
-
const fields = this.model.projection?.
|
|
268
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
264
269
|
|
|
265
270
|
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
266
271
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
@@ -269,7 +274,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
269
274
|
continue;
|
|
270
275
|
}
|
|
271
276
|
|
|
272
|
-
this.createDataCell(cells, this.model.
|
|
277
|
+
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
@@ -278,8 +283,8 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
278
283
|
|
|
279
284
|
private getDraftRowCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
280
285
|
const cells: DxGridPlaneCells = {};
|
|
281
|
-
const fields = this.model.projection?.
|
|
282
|
-
const draftRows = this.model.
|
|
286
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
287
|
+
const draftRows = this.model.getDraftRows();
|
|
283
288
|
|
|
284
289
|
// Return cells of the CTA row if no draft row is active
|
|
285
290
|
if (draftRows.length === 0) {
|
|
@@ -319,22 +324,22 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
319
324
|
|
|
320
325
|
private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
321
326
|
const cells: DxGridPlaneCells = {};
|
|
322
|
-
const fields = this.model.projection?.
|
|
327
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
328
|
+
const currentSort = this.model.getSorting();
|
|
323
329
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
324
330
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
325
|
-
const
|
|
326
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : undefined;
|
|
331
|
+
const direction = currentSort?.fieldId === field.id ? currentSort.direction : undefined;
|
|
327
332
|
|
|
328
333
|
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
329
334
|
// TODO(burdon): Use same logic as form for fallback title.
|
|
330
335
|
value: '',
|
|
331
336
|
resizeHandle: 'col',
|
|
332
337
|
accessoryHtml: `
|
|
333
|
-
<span class="grow min-
|
|
338
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
334
339
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
335
340
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
336
341
|
`,
|
|
337
|
-
className: '!bg-
|
|
342
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
338
343
|
};
|
|
339
344
|
}
|
|
340
345
|
|
|
@@ -377,7 +382,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
377
382
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
378
383
|
return {
|
|
379
384
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
380
|
-
className: '!bg-
|
|
385
|
+
className: '!bg-toolbar-surface',
|
|
381
386
|
readonly: true,
|
|
382
387
|
value: '',
|
|
383
388
|
},
|
|
@@ -389,9 +394,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
389
394
|
accessoryHtml: tableControls.checkbox.render({
|
|
390
395
|
rowIndex: 0,
|
|
391
396
|
header: true,
|
|
392
|
-
checked: this.model.selection.
|
|
397
|
+
checked: this.model.selection.allRowsSelected,
|
|
393
398
|
}),
|
|
394
|
-
className: '!bg-
|
|
399
|
+
className: '!bg-toolbar-surface',
|
|
395
400
|
readonly: true,
|
|
396
401
|
value: '',
|
|
397
402
|
},
|
|
@@ -403,10 +408,10 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
403
408
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
404
409
|
accessoryHtml: this.model.features.schemaEditable
|
|
405
410
|
? tableButtons.addColumn.render({
|
|
406
|
-
disabled: (this.model.projection?.
|
|
411
|
+
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
407
412
|
})
|
|
408
413
|
: undefined,
|
|
409
|
-
className: '!bg-
|
|
414
|
+
className: '!bg-toolbar-surface',
|
|
410
415
|
readonly: true,
|
|
411
416
|
value: '',
|
|
412
417
|
},
|
|
@@ -415,7 +420,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
415
420
|
|
|
416
421
|
private getDraftActionCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
417
422
|
const cells: DxGridPlaneCells = {};
|
|
418
|
-
const draftRows = this.model.
|
|
423
|
+
const draftRows = this.model.getDraftRows();
|
|
419
424
|
|
|
420
425
|
// Return cells of the CTA row if no draft row is active
|
|
421
426
|
if (draftRows.length === 0) {
|
|
@@ -442,7 +447,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
442
447
|
|
|
443
448
|
private getDraftIconCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
444
449
|
const cells: DxGridPlaneCells = {};
|
|
445
|
-
const draftRows = this.model.
|
|
450
|
+
const draftRows = this.model.getDraftRows();
|
|
446
451
|
|
|
447
452
|
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
448
453
|
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
@@ -460,7 +465,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
460
465
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
461
466
|
if (!selected) {
|
|
462
467
|
if (selectionMode === 'single') {
|
|
463
|
-
return ['
|
|
468
|
+
return ['cursor-pointer!'];
|
|
464
469
|
} else {
|
|
465
470
|
return undefined;
|
|
466
471
|
}
|
|
@@ -468,9 +473,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
468
473
|
|
|
469
474
|
switch (selectionMode) {
|
|
470
475
|
case 'single':
|
|
471
|
-
|
|
472
|
-
return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
|
|
476
|
+
return ['dx-grid__cell--no-focus-unfurl !bg-current-surface hover:bg-hover-surface cursor-pointer!'];
|
|
473
477
|
case 'multiple':
|
|
474
|
-
return ['!bg-
|
|
478
|
+
return ['!bg-grid-cell-selected'];
|
|
475
479
|
}
|
|
476
480
|
};
|
|
@@ -17,6 +17,7 @@ const TABLE_SELECTORS = {
|
|
|
17
17
|
},
|
|
18
18
|
columnDelete: 'column-delete',
|
|
19
19
|
rowDelete: 'row-menu-delete',
|
|
20
|
+
addRow: 'table.toolbar.add-row',
|
|
20
21
|
} as const;
|
|
21
22
|
|
|
22
23
|
export class TableManager {
|
|
@@ -53,12 +54,18 @@ export class TableManager {
|
|
|
53
54
|
await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
public async
|
|
57
|
+
public async addRow(): Promise<void> {
|
|
58
|
+
await this.page.getByTestId(TABLE_SELECTORS.addRow).click();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async addColumn({ label, format }: { label: string; format?: string }): Promise<void> {
|
|
57
62
|
await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await this.page.
|
|
63
|
+
if (format) {
|
|
64
|
+
await this.page.getByRole('combobox').click();
|
|
65
|
+
await this.page.getByLabel(format).click();
|
|
66
|
+
}
|
|
67
|
+
await this.page.getByPlaceholder('Property label').click();
|
|
68
|
+
await this.page.getByPlaceholder('Property label').fill(label);
|
|
69
|
+
await this.page.getByRole('button', { name: 'Save' }).click();
|
|
63
70
|
}
|
|
64
71
|
}
|
|
@@ -10,7 +10,7 @@ export default defineConfig({
|
|
|
10
10
|
...e2ePreset(import.meta.dirname),
|
|
11
11
|
// TODO(wittjosiah): Avoid hard-coding ports.
|
|
12
12
|
webServer: {
|
|
13
|
-
command: '
|
|
13
|
+
command: 'pnpm storybook dev --ci --quiet --port=9004 --config-dir=.storybook',
|
|
14
14
|
port: 9004,
|
|
15
15
|
reuseExistingServer: false,
|
|
16
16
|
},
|
|
@@ -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');
|
|
@@ -71,6 +71,7 @@ test.describe('Table', () => {
|
|
|
71
71
|
const table = new TableManager(page);
|
|
72
72
|
|
|
73
73
|
await table.grid.ready();
|
|
74
|
+
await expect(page.getByRole('gridcell', { name: 'Sapiente.' })).toHaveCount(1);
|
|
74
75
|
await table.deleteRow(0);
|
|
75
76
|
await expect(page.getByRole('gridcell', { name: 'Sapiente.' })).toHaveCount(0);
|
|
76
77
|
await page.close();
|
|
@@ -88,7 +89,7 @@ test.describe('Table', () => {
|
|
|
88
89
|
// Delete action affects all selected rows.
|
|
89
90
|
await table.deleteRow(0);
|
|
90
91
|
|
|
91
|
-
await expect(page.getByRole('gridcell', { name: '
|
|
92
|
+
await expect(page.getByRole('gridcell', { name: 'Aut.' })).toHaveCount(0);
|
|
92
93
|
await expect(page.getByRole('gridcell', { name: 'Beatae.' })).toHaveCount(0);
|
|
93
94
|
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(0);
|
|
94
95
|
await page.close();
|
|
@@ -120,8 +121,22 @@ test.describe('Table', () => {
|
|
|
120
121
|
await page.close();
|
|
121
122
|
});
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
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 }) => {
|
|
125
140
|
test.skip(browserName === 'webkit');
|
|
126
141
|
test.skip(browserName === 'firefox');
|
|
127
142
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
@@ -130,7 +145,7 @@ test.describe('Table', () => {
|
|
|
130
145
|
await table.grid.ready();
|
|
131
146
|
const newColumnLabel = 'TEST LABEL';
|
|
132
147
|
|
|
133
|
-
await table.addColumn({ label: newColumnLabel, format: '
|
|
148
|
+
await table.addColumn({ label: newColumnLabel, format: 'Number' });
|
|
134
149
|
|
|
135
150
|
await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
|
|
136
151
|
await page.close();
|
|
@@ -153,13 +168,43 @@ test.describe('Table', () => {
|
|
|
153
168
|
// Assert the first two switch checkboxes are checked.
|
|
154
169
|
await expect(page.getByTestId('table-switch').first()).toBeChecked();
|
|
155
170
|
await expect(page.getByTestId('table-switch').nth(1)).toBeChecked();
|
|
156
|
-
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('
|
|
171
|
+
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('Aut.');
|
|
157
172
|
await expect(table.grid.cell(0, 1, 'grid')).toHaveText('Beatae.');
|
|
158
173
|
|
|
159
174
|
await page.close();
|
|
160
175
|
});
|
|
161
176
|
|
|
162
|
-
test('
|
|
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
|
+
|
|
206
|
+
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
207
|
+
test.skip('extant relations work as expected', async ({ browser, browserName }) => {
|
|
163
208
|
test.skip(browserName === 'webkit');
|
|
164
209
|
test.skip(browserName === 'firefox');
|
|
165
210
|
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
@@ -187,7 +232,7 @@ test.describe('Table', () => {
|
|
|
187
232
|
// Click on the combobox to open options
|
|
188
233
|
await page.getByRole('combobox').click();
|
|
189
234
|
await page.pause();
|
|
190
|
-
await page.getByPlaceholder('Search
|
|
235
|
+
await page.getByPlaceholder('Search…').focus();
|
|
191
236
|
|
|
192
237
|
// Type the first few letters of an org name.
|
|
193
238
|
const orgName =
|
|
@@ -213,7 +258,8 @@ test.describe('Table', () => {
|
|
|
213
258
|
await page.close();
|
|
214
259
|
});
|
|
215
260
|
|
|
216
|
-
|
|
261
|
+
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
262
|
+
test.skip('new relations work as expected', async ({ browser, browserName }) => {
|
|
217
263
|
test.skip(browserName === 'webkit');
|
|
218
264
|
test.skip(browserName === 'firefox');
|
|
219
265
|
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
@@ -240,7 +286,7 @@ test.describe('Table', () => {
|
|
|
240
286
|
|
|
241
287
|
// Click on the combobox to open options
|
|
242
288
|
await page.getByRole('combobox').click();
|
|
243
|
-
await page.getByPlaceholder('Search
|
|
289
|
+
await page.getByPlaceholder('Search…').focus();
|
|
244
290
|
|
|
245
291
|
// Type the first few letters of an org name.
|
|
246
292
|
const orgName = 'Sally';
|
|
@@ -254,7 +300,7 @@ test.describe('Table', () => {
|
|
|
254
300
|
await page.pause();
|
|
255
301
|
|
|
256
302
|
// Click the save button in the popover
|
|
257
|
-
await page.getByTestId('save-button').click();
|
|
303
|
+
await page.getByTestId('create-referenced-object-form').getByTestId('save-button').click();
|
|
258
304
|
|
|
259
305
|
// Assert that the cell element has the org name
|
|
260
306
|
await expect(targetCell).toHaveText(orgName);
|
|
@@ -264,7 +310,7 @@ test.describe('Table', () => {
|
|
|
264
310
|
dxGridElement.scrollToColumn(6);
|
|
265
311
|
});
|
|
266
312
|
|
|
267
|
-
const nonRefContent =
|
|
313
|
+
const nonRefContent = random.lorem.words(3);
|
|
268
314
|
const nonRefCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="6"]');
|
|
269
315
|
await nonRefCell.click();
|
|
270
316
|
await page.keyboard.press('Enter');
|