@dxos/react-ui-table 0.8.4-main.ae835ea → 0.8.4-main.bc674ce
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-G7MGPBSR.mjs → chunk-I5IFW7UF.mjs} +633 -663
- package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +7 -0
- package/dist/lib/browser/chunk-OZBLY7XM.mjs +76 -0
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1131 -999
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +29 -41
- 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-LD5JEBA6.mjs → chunk-DDJKYJFY.mjs} +633 -662
- package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs +77 -0
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1131 -999
- 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 +29 -41
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +4 -5
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +4 -18
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +12 -9
- 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 +8 -8
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +71 -5
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +4 -4
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +4 -4
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -15
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +8 -8
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +102 -23
- 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 +1 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +4 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +31 -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 +13 -11
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +53 -53
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +6 -6
- package/src/components/Table/DynamicTable.stories.tsx +12 -13
- package/src/components/Table/DynamicTable.tsx +21 -14
- package/src/components/Table/Relations.stories.tsx +35 -39
- package/src/components/Table/RowActionsMenu.tsx +24 -15
- package/src/components/Table/Table.stories.tsx +71 -75
- package/src/components/Table/Table.tsx +335 -305
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +110 -133
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +16 -12
- package/src/components/TableCellEditor/TableCellEditor.tsx +31 -31
- package/src/components/TableToolbar/TableToolbar.tsx +44 -31
- package/src/components/testing.ts +38 -12
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +35 -33
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +82 -54
- package/src/model/table-model.ts +428 -163
- package/src/model/table-presentation.test.ts +59 -49
- package/src/model/table-presentation.ts +31 -26
- package/src/playwright/TableManager.ts +5 -5
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +8 -5
- package/src/testing/useTestTableModel.ts +44 -37
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +90 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.ts +39 -27
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +19 -0
- package/src/util/sort.ts +90 -0
- package/dist/lib/browser/chunk-4K7JPALT.mjs +0 -66
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
- package/dist/lib/browser/chunk-G7MGPBSR.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-LD5JEBA6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs +0 -67
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +0 -7
- 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/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Registry } from '@effect-atom/atom-react';
|
|
5
6
|
import * as Schema from 'effect/Schema';
|
|
6
7
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
8
|
|
|
8
|
-
import { Filter, Query } from '@dxos/echo';
|
|
9
|
-
import { TypedObject } from '@dxos/echo/internal';
|
|
9
|
+
import { Filter, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import {
|
|
12
|
-
import { createView } from '@dxos/schema';
|
|
11
|
+
import { ProjectionModel, View, createDirectChangeCallback } from '@dxos/schema';
|
|
13
12
|
|
|
14
13
|
import { Table } from '../types';
|
|
15
14
|
|
|
@@ -17,48 +16,53 @@ import { TableModel, type TableModelProps } from './table-model';
|
|
|
17
16
|
import { TablePresentation } from './table-presentation';
|
|
18
17
|
|
|
19
18
|
describe('TablePresentation', () => {
|
|
20
|
-
describe('
|
|
19
|
+
describe('getCells', () => {
|
|
20
|
+
let registry: Registry.Registry;
|
|
21
21
|
let data: any[];
|
|
22
|
-
let updateCount: number;
|
|
23
22
|
let model: any;
|
|
24
23
|
let presentation: TablePresentation;
|
|
25
24
|
|
|
26
25
|
beforeEach(async () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
model = createTableModel({
|
|
36
|
-
onCellUpdate: () => updateCount++,
|
|
37
|
-
});
|
|
38
|
-
model.setRows(data);
|
|
26
|
+
registry = Registry.make();
|
|
27
|
+
data = [
|
|
28
|
+
{ id: '1', title: 'A', count: 1 },
|
|
29
|
+
{ id: '2', title: 'B', count: 2 },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
model = createTableModel(registry, {});
|
|
39
33
|
await model.open();
|
|
40
|
-
|
|
34
|
+
model.setRows(data);
|
|
35
|
+
presentation = new TablePresentation(registry, model);
|
|
41
36
|
});
|
|
42
37
|
|
|
43
|
-
it('should
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
it('should return cells for the visible range', () => {
|
|
39
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
40
|
+
|
|
41
|
+
// Verify that cells are returned for the range. The exact column indices depend on the projection.
|
|
42
|
+
const cellKeys = Object.keys(cells);
|
|
43
|
+
expect(cellKeys.length).toBeGreaterThan(0);
|
|
44
|
+
|
|
45
|
+
// Check that values from the data are present in the cells.
|
|
46
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
47
|
+
expect(values).toContain('A');
|
|
48
|
+
expect(values).toContain('B');
|
|
49
|
+
});
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
it('should reflect row data changes', () => {
|
|
52
|
+
// Update the row data and refresh.
|
|
48
53
|
data[0].title = 'New Value';
|
|
49
|
-
|
|
54
|
+
model.setRows(data);
|
|
50
55
|
|
|
51
56
|
// Verify the new value through getCells.
|
|
52
|
-
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col:
|
|
53
|
-
|
|
57
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 2 } }, 'grid');
|
|
58
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
59
|
+
expect(values).toContain('New Value');
|
|
54
60
|
});
|
|
55
61
|
|
|
56
|
-
it('should
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
data.push({ title: 'C', count: 3 });
|
|
61
|
-
expect(updateCount).toBe(2);
|
|
62
|
+
it('should reflect new rows', () => {
|
|
63
|
+
// Add a new row.
|
|
64
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
65
|
+
model.setRows(data);
|
|
62
66
|
|
|
63
67
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
64
68
|
|
|
@@ -67,17 +71,12 @@ describe('TablePresentation', () => {
|
|
|
67
71
|
expect(cells['1,2']?.value).toBe('3');
|
|
68
72
|
});
|
|
69
73
|
|
|
70
|
-
it('should handle
|
|
71
|
-
//
|
|
72
|
-
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
73
|
-
|
|
74
|
-
// Multiple operations.
|
|
74
|
+
it('should handle row modifications', () => {
|
|
75
|
+
// Modify the data.
|
|
75
76
|
data[0].title = 'Updated A';
|
|
76
|
-
data.push({ title: 'C', count: 3
|
|
77
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
77
78
|
data.splice(1, 1);
|
|
78
|
-
|
|
79
|
-
// We expect one update per operation affecting visible rows.
|
|
80
|
-
expect(updateCount).toBe(4);
|
|
79
|
+
model.setRows(data);
|
|
81
80
|
|
|
82
81
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
83
82
|
|
|
@@ -88,18 +87,29 @@ describe('TablePresentation', () => {
|
|
|
88
87
|
});
|
|
89
88
|
});
|
|
90
89
|
|
|
91
|
-
|
|
90
|
+
const Test = Schema.Struct({
|
|
92
91
|
title: Schema.String,
|
|
93
92
|
count: Schema.Number,
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
}).pipe(
|
|
94
|
+
Type.object({
|
|
95
|
+
typename: 'example.com/type/Test',
|
|
96
|
+
version: '0.1.0',
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
97
101
|
const schema = createEchoSchema(Test);
|
|
98
|
-
const
|
|
99
|
-
const view = createView({
|
|
102
|
+
const view = View.make({
|
|
100
103
|
query: Query.select(Filter.type(schema)),
|
|
101
104
|
jsonSchema: schema.jsonSchema,
|
|
102
|
-
presentation: table,
|
|
103
105
|
});
|
|
104
|
-
|
|
106
|
+
const object = Table.make({ view });
|
|
107
|
+
const projection = new ProjectionModel({
|
|
108
|
+
registry,
|
|
109
|
+
view,
|
|
110
|
+
baseSchema: schema.jsonSchema,
|
|
111
|
+
change: createDirectChangeCallback(view.projection, schema.jsonSchema),
|
|
112
|
+
});
|
|
113
|
+
projection.normalizeView();
|
|
114
|
+
return new TableModel({ registry, object, projection, ...props });
|
|
105
115
|
};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import {
|
|
9
|
+
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
10
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
11
|
import {
|
|
12
12
|
type DxGridCellValue,
|
|
@@ -15,8 +15,8 @@ import {
|
|
|
15
15
|
type DxGridPlaneRange,
|
|
16
16
|
toPlaneCellIndex,
|
|
17
17
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
19
18
|
import { type FieldType, VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
19
|
+
import { mx } from '@dxos/ui-theme';
|
|
20
20
|
|
|
21
21
|
import { tableButtons, tableControls } from '../util';
|
|
22
22
|
|
|
@@ -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':
|
|
@@ -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
|
}
|
|
@@ -201,7 +206,7 @@ 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();
|
|
@@ -210,7 +215,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
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,11 +324,11 @@ 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.
|
|
@@ -389,7 +394,7 @@ 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
399
|
className: '!bg-toolbarSurface',
|
|
395
400
|
readonly: true,
|
|
@@ -403,7 +408,7 @@ 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
414
|
className: '!bg-toolbarSurface',
|
|
@@ -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 })] = {
|
|
@@ -53,12 +53,12 @@ export class TableManager {
|
|
|
53
53
|
await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
public async addColumn({ label, format }: { label: string; format:
|
|
56
|
+
public async addColumn({ label, format }: { label: string; format: string }): Promise<void> {
|
|
57
57
|
await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
|
|
58
58
|
await this.page.getByRole('combobox').click();
|
|
59
|
-
await this.page.getByLabel(
|
|
60
|
-
await this.page.getByPlaceholder('Property label
|
|
61
|
-
await this.page.getByPlaceholder('Property label
|
|
62
|
-
await this.page.getByRole('button', { name: '
|
|
59
|
+
await this.page.getByLabel(format).click();
|
|
60
|
+
await this.page.getByPlaceholder('Property label').click();
|
|
61
|
+
await this.page.getByPlaceholder('Property label').fill(label);
|
|
62
|
+
await this.page.getByRole('button', { name: 'Save' }).click();
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -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: 'moon run storybook:serve-e2e -- --port=9004',
|
|
13
|
+
command: 'moon run storybook-react:serve-e2e -- --port=9004',
|
|
14
14
|
port: 9004,
|
|
15
15
|
reuseExistingServer: false,
|
|
16
16
|
},
|
|
@@ -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();
|
|
@@ -130,7 +131,7 @@ test.describe('Table', () => {
|
|
|
130
131
|
await table.grid.ready();
|
|
131
132
|
const newColumnLabel = 'TEST LABEL';
|
|
132
133
|
|
|
133
|
-
await table.addColumn({ label: newColumnLabel, format: '
|
|
134
|
+
await table.addColumn({ label: newColumnLabel, format: 'Number' });
|
|
134
135
|
|
|
135
136
|
await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
|
|
136
137
|
await page.close();
|
|
@@ -153,13 +154,14 @@ test.describe('Table', () => {
|
|
|
153
154
|
// Assert the first two switch checkboxes are checked.
|
|
154
155
|
await expect(page.getByTestId('table-switch').first()).toBeChecked();
|
|
155
156
|
await expect(page.getByTestId('table-switch').nth(1)).toBeChecked();
|
|
156
|
-
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('
|
|
157
|
+
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('Aut.');
|
|
157
158
|
await expect(table.grid.cell(0, 1, 'grid')).toHaveText('Beatae.');
|
|
158
159
|
|
|
159
160
|
await page.close();
|
|
160
161
|
});
|
|
161
162
|
|
|
162
|
-
|
|
163
|
+
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
164
|
+
test.skip('extant relations work as expected', async ({ browser, browserName }) => {
|
|
163
165
|
test.skip(browserName === 'webkit');
|
|
164
166
|
test.skip(browserName === 'firefox');
|
|
165
167
|
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
@@ -213,7 +215,8 @@ test.describe('Table', () => {
|
|
|
213
215
|
await page.close();
|
|
214
216
|
});
|
|
215
217
|
|
|
216
|
-
|
|
218
|
+
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
219
|
+
test.skip('new relations work as expected', async ({ browser, browserName }) => {
|
|
217
220
|
test.skip(browserName === 'webkit');
|
|
218
221
|
test.skip(browserName === 'firefox');
|
|
219
222
|
const { page } = await setupPage(browser, { url: relationsStoryUrl });
|
|
@@ -2,43 +2,52 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import { type RefObject, useCallback, useContext, useMemo, useRef } from 'react';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { type Database, type Type } from '@dxos/echo';
|
|
9
|
+
import { isMutable } from '@dxos/echo/internal';
|
|
9
10
|
import { faker } from '@dxos/random';
|
|
10
|
-
import { useClient } from '@dxos/react-client';
|
|
11
11
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { useClientStory } from '@dxos/react-client/testing';
|
|
13
|
+
import { useGlobalFilteredObjects } from '@dxos/react-ui-searchlist';
|
|
14
|
+
import { type ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
|
|
14
15
|
|
|
15
16
|
import { type TableController } from '../components';
|
|
16
|
-
import { useAddRow, useTableModel } from '../hooks';
|
|
17
|
-
import { TablePresentation } from '../model';
|
|
17
|
+
import { useAddRow, useProjectionModel, useTableModel } from '../hooks';
|
|
18
|
+
import { type TableModel, TablePresentation } from '../model';
|
|
19
|
+
import { Table } from '../types';
|
|
18
20
|
|
|
19
21
|
faker.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
|
|
20
22
|
|
|
23
|
+
export type TestTableModel<T extends Type.Obj.Any = Type.Obj.Any> = {
|
|
24
|
+
schema: T | undefined;
|
|
25
|
+
table: Table.Table | undefined;
|
|
26
|
+
projection: ProjectionModel | undefined;
|
|
27
|
+
tableRef: RefObject<TableController | null>;
|
|
28
|
+
model: TableModel | undefined;
|
|
29
|
+
presentation: TablePresentation | undefined;
|
|
30
|
+
db: Database.Database | undefined;
|
|
31
|
+
handleInsertRow: () => void;
|
|
32
|
+
handleSaveView: () => void;
|
|
33
|
+
handleDeleteRows: (rowIndex: number, objects: any[]) => void;
|
|
34
|
+
handleDeleteColumn: (fieldId: string) => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
21
37
|
/**
|
|
22
38
|
* Custom hook to create and manage a test table model for storybook demonstrations.
|
|
23
39
|
* Provides table data, schema, and handlers for table operations.
|
|
24
40
|
*/
|
|
25
|
-
export const useTestTableModel = () => {
|
|
26
|
-
const
|
|
27
|
-
const { space } =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const projection = useMemo(() => {
|
|
36
|
-
if (schema && view?.projection) {
|
|
37
|
-
const projection = new ProjectionModel(toJsonSchema(schema), view.projection);
|
|
38
|
-
projection.normalizeView();
|
|
39
|
-
return projection;
|
|
40
|
-
}
|
|
41
|
-
}, [schema, view?.projection]);
|
|
41
|
+
export const useTestTableModel = <T extends Type.Obj.Any = Type.Obj.Any>(): TestTableModel<T> => {
|
|
42
|
+
const registry = useContext(RegistryContext);
|
|
43
|
+
const { space } = useClientStory();
|
|
44
|
+
const db = space?.db;
|
|
45
|
+
|
|
46
|
+
const tables = useQuery(space?.db, Filter.type(Table.Table));
|
|
47
|
+
const table = tables.at(0);
|
|
48
|
+
const typename = table?.view.target?.query ? getTypenameFromQuery(table.view.target.query.ast) : undefined;
|
|
49
|
+
const schema = useSchema<T>(space?.db, typename);
|
|
50
|
+
const projection = useProjectionModel(schema, table, registry);
|
|
42
51
|
|
|
43
52
|
const features = useMemo(
|
|
44
53
|
() => ({
|
|
@@ -49,7 +58,7 @@ export const useTestTableModel = () => {
|
|
|
49
58
|
[schema],
|
|
50
59
|
);
|
|
51
60
|
|
|
52
|
-
const objects = useQuery(
|
|
61
|
+
const objects = useQuery(db, schema ? Filter.type(schema) : Filter.nothing());
|
|
53
62
|
const filteredObjects = useGlobalFilteredObjects(objects);
|
|
54
63
|
|
|
55
64
|
const tableRef = useRef<TableController>(null);
|
|
@@ -61,15 +70,15 @@ export const useTestTableModel = () => {
|
|
|
61
70
|
tableRef.current?.update?.();
|
|
62
71
|
}, []);
|
|
63
72
|
|
|
64
|
-
const addRow = useAddRow({
|
|
73
|
+
const addRow = useAddRow({ db, schema });
|
|
65
74
|
|
|
66
75
|
const handleDeleteRows = useCallback(
|
|
67
76
|
(_: number, objects: any[]) => {
|
|
68
77
|
for (const object of objects) {
|
|
69
|
-
|
|
78
|
+
db?.remove(object);
|
|
70
79
|
}
|
|
71
80
|
},
|
|
72
|
-
[
|
|
81
|
+
[db],
|
|
73
82
|
);
|
|
74
83
|
|
|
75
84
|
const handleDeleteColumn = useCallback(
|
|
@@ -82,8 +91,7 @@ export const useTestTableModel = () => {
|
|
|
82
91
|
);
|
|
83
92
|
|
|
84
93
|
const model = useTableModel({
|
|
85
|
-
|
|
86
|
-
schema: jsonSchema,
|
|
94
|
+
object: table,
|
|
87
95
|
projection,
|
|
88
96
|
features,
|
|
89
97
|
rows: filteredObjects,
|
|
@@ -97,7 +105,7 @@ export const useTestTableModel = () => {
|
|
|
97
105
|
const handleInsertRow = useCallback(() => {
|
|
98
106
|
const insertResult = model?.insertRow();
|
|
99
107
|
tableRef.current?.handleInsertRowResult?.(insertResult);
|
|
100
|
-
}, [model
|
|
108
|
+
}, [model]);
|
|
101
109
|
|
|
102
110
|
const handleSaveView = useCallback(() => {
|
|
103
111
|
model?.saveView();
|
|
@@ -105,19 +113,18 @@ export const useTestTableModel = () => {
|
|
|
105
113
|
|
|
106
114
|
const presentation = useMemo(() => {
|
|
107
115
|
if (model) {
|
|
108
|
-
return new TablePresentation(model);
|
|
116
|
+
return new TablePresentation(registry, model);
|
|
109
117
|
}
|
|
110
|
-
}, [model]);
|
|
118
|
+
}, [registry, model]);
|
|
111
119
|
|
|
112
120
|
return {
|
|
113
121
|
schema,
|
|
114
|
-
|
|
122
|
+
table,
|
|
115
123
|
projection,
|
|
116
124
|
tableRef,
|
|
117
125
|
model,
|
|
118
126
|
presentation,
|
|
119
|
-
|
|
120
|
-
client,
|
|
127
|
+
db,
|
|
121
128
|
handleInsertRow,
|
|
122
129
|
handleSaveView,
|
|
123
130
|
handleDeleteRows,
|
package/src/translations.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Resource } from '@dxos/react-ui';
|
|
6
6
|
|
|
7
|
-
export const translationKey = 'react-ui-table';
|
|
7
|
+
export const translationKey = '@dxos/react-ui-table';
|
|
8
8
|
|
|
9
9
|
export const translations = [
|
|
10
10
|
{
|
|
@@ -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': 'Type',
|
|
18
|
+
'new schema': 'New type',
|
|
19
19
|
'continue label': 'Continue',
|
|
20
20
|
'create stack section label': 'Create table',
|
|
21
21
|
'add row label': 'Add row',
|