@dxos/react-ui-table 0.8.4-main.e8ec1fe → 0.8.4-main.effb148878
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/LICENSE +102 -5
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GS2UNGJK.mjs → chunk-KC62K4WS.mjs} +643 -722
- package/dist/lib/browser/chunk-KC62K4WS.mjs.map +7 -0
- package/dist/lib/browser/chunk-VWQWF3CP.mjs +67 -0
- package/dist/lib/browser/chunk-VWQWF3CP.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1103 -1023
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +35 -46
- 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-TRR3C5NX.mjs → chunk-62FOU4M7.mjs} +643 -721
- package/dist/lib/node-esm/chunk-62FOU4M7.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/chunk-UBXOF64L.mjs +68 -0
- package/dist/lib/node-esm/chunk-UBXOF64L.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1103 -1023
- 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 +35 -46
- 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 +6 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +22 -37
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +85 -80
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +20 -21
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +93 -26
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +18 -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 +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 +24 -26
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +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 +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 -1
- 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 +103 -24
- 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 +32 -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 -11
- 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.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 +69 -79
- package/src/components/Table/ColumnActionsMenu.tsx +17 -12
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +32 -32
- package/src/components/Table/DynamicTable.tsx +32 -33
- package/src/components/Table/Relations.stories.tsx +104 -64
- package/src/components/Table/RowActionsMenu.tsx +6 -4
- package/src/components/Table/Table.stories.tsx +125 -112
- package/src/components/Table/Table.tsx +43 -407
- package/src/components/Table/TableContent.tsx +426 -0
- package/src/components/{TableToolbar → Table}/TableToolbar.tsx +44 -45
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
- package/src/components/TableCellEditor/FormCellEditor.tsx +120 -136
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +25 -19
- package/src/components/TableCellEditor/TableCellEditor.tsx +35 -33
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +38 -17
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +38 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/index.ts +0 -4
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +77 -55
- package/src/model/table-model.ts +431 -168
- package/src/model/table-presentation.test.ts +54 -50
- package/src/model/table-presentation.ts +44 -40
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +52 -8
- package/src/testing/useTestTableModel.ts +49 -43
- package/src/translations.ts +20 -22
- package/src/types/Table.ts +81 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +63 -0
- package/src/util/dynamic-table.ts +48 -33
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- 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-2PFAPQL4.mjs +0 -66
- package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +0 -7
- package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs +0 -67
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +0 -7
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/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/TableToolbar/index.ts +0 -5
- package/src/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
|
@@ -2,63 +2,66 @@
|
|
|
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 { DXN, Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import {
|
|
12
|
-
import { View } from '@dxos/schema';
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
13
12
|
|
|
14
13
|
import { Table } from '../types';
|
|
15
|
-
|
|
16
14
|
import { TableModel, type TableModelProps } from './table-model';
|
|
17
15
|
import { TablePresentation } from './table-presentation';
|
|
18
16
|
|
|
19
17
|
describe('TablePresentation', () => {
|
|
20
|
-
describe('
|
|
18
|
+
describe('getCells', () => {
|
|
19
|
+
let registry: Registry.Registry;
|
|
21
20
|
let data: any[];
|
|
22
|
-
let updateCount: number;
|
|
23
21
|
let model: any;
|
|
24
22
|
let presentation: TablePresentation;
|
|
25
23
|
|
|
26
24
|
beforeEach(async () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
model = createTableModel({
|
|
36
|
-
onCellUpdate: () => updateCount++,
|
|
37
|
-
});
|
|
38
|
-
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, {});
|
|
39
32
|
await model.open();
|
|
40
|
-
|
|
33
|
+
model.setRows(data);
|
|
34
|
+
presentation = new TablePresentation(registry, model);
|
|
41
35
|
});
|
|
42
36
|
|
|
43
|
-
it('should
|
|
44
|
-
|
|
45
|
-
|
|
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');
|
|
39
|
+
|
|
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);
|
|
43
|
+
|
|
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');
|
|
48
|
+
});
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
it('should reflect row data changes', () => {
|
|
51
|
+
// Update the row data and refresh.
|
|
48
52
|
data[0].title = 'New Value';
|
|
49
|
-
|
|
53
|
+
model.setRows(data);
|
|
50
54
|
|
|
51
55
|
// Verify the new value through getCells.
|
|
52
|
-
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col:
|
|
53
|
-
|
|
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');
|
|
54
59
|
});
|
|
55
60
|
|
|
56
|
-
it('should
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
data.push({ title: 'C', count: 3 });
|
|
61
|
-
expect(updateCount).toBe(2);
|
|
61
|
+
it('should reflect new rows', () => {
|
|
62
|
+
// Add a new row.
|
|
63
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
64
|
+
model.setRows(data);
|
|
62
65
|
|
|
63
66
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
64
67
|
|
|
@@ -67,17 +70,12 @@ describe('TablePresentation', () => {
|
|
|
67
70
|
expect(cells['1,2']?.value).toBe('3');
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
it('should handle
|
|
71
|
-
//
|
|
72
|
-
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
73
|
-
|
|
74
|
-
// Multiple operations.
|
|
73
|
+
it('should handle row modifications', () => {
|
|
74
|
+
// Modify the data.
|
|
75
75
|
data[0].title = 'Updated A';
|
|
76
|
-
data.push({ title: 'C', count: 3
|
|
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(4);
|
|
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
|
|
|
@@ -88,18 +86,24 @@ describe('TablePresentation', () => {
|
|
|
88
86
|
});
|
|
89
87
|
});
|
|
90
88
|
|
|
91
|
-
|
|
89
|
+
const Test = Schema.Struct({
|
|
92
90
|
title: Schema.String,
|
|
93
91
|
count: Schema.Number,
|
|
94
|
-
})
|
|
92
|
+
}).pipe(Type.makeObject(DXN.make('com.example.type.test', '0.1.0')));
|
|
95
93
|
|
|
96
|
-
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
97
|
-
const schema = createEchoSchema(Test);
|
|
98
|
-
const
|
|
99
|
-
const view = View.make({
|
|
94
|
+
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
95
|
+
const schema = createEchoSchema(Type.getSchema(Test));
|
|
96
|
+
const view = ViewModel.make({
|
|
100
97
|
query: Query.select(Filter.type(schema)),
|
|
101
98
|
jsonSchema: schema.jsonSchema,
|
|
102
|
-
presentation: table,
|
|
103
99
|
});
|
|
104
|
-
|
|
100
|
+
const object = Table.make({ view });
|
|
101
|
+
const projection = new ProjectionModel({
|
|
102
|
+
registry,
|
|
103
|
+
view,
|
|
104
|
+
baseSchema: schema.jsonSchema,
|
|
105
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(Type.getSchema(schema))),
|
|
106
|
+
});
|
|
107
|
+
projection.normalizeView();
|
|
108
|
+
return new TableModel({ registry, object, projection, ...props });
|
|
105
109
|
};
|
|
@@ -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
|
-
import { Obj } from '@dxos/echo';
|
|
9
|
-
import {
|
|
8
|
+
import { Obj, type View } from '@dxos/echo';
|
|
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,11 +15,10 @@ import {
|
|
|
15
15
|
type DxGridPlaneRange,
|
|
16
16
|
toPlaneCellIndex,
|
|
17
17
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
19
|
+
import { mx } from '@dxos/ui-theme';
|
|
20
20
|
|
|
21
21
|
import { tableButtons, tableControls } from '../util';
|
|
22
|
-
|
|
23
22
|
import { type SelectionMode } from './selection-model';
|
|
24
23
|
import { type TableModel, type TableRow } from './table-model';
|
|
25
24
|
|
|
@@ -29,15 +28,20 @@ import { type TableModel, type TableRow } from './table-model';
|
|
|
29
28
|
* different grid planes.
|
|
30
29
|
*/
|
|
31
30
|
export class TablePresentation<T extends TableRow = TableRow> {
|
|
31
|
+
private readonly _registry: Registry.Registry;
|
|
32
|
+
private readonly _visibleRange: Atom.Writable<DxGridPlaneRange>;
|
|
32
33
|
private fieldProjectionCache = new Map<string, ReturnType<typeof this.model.projection.getFieldProjection>>();
|
|
33
34
|
|
|
34
35
|
constructor(
|
|
36
|
+
registry: Registry.Registry,
|
|
35
37
|
private readonly model: TableModel<T>,
|
|
36
|
-
|
|
38
|
+
) {
|
|
39
|
+
this._registry = registry;
|
|
40
|
+
this._visibleRange = Atom.make<DxGridPlaneRange>({
|
|
37
41
|
start: { row: 0, col: 0 },
|
|
38
42
|
end: { row: 0, col: 0 },
|
|
39
|
-
})
|
|
40
|
-
|
|
43
|
+
});
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
|
|
43
47
|
// Clear cache at the start of each render pass
|
|
@@ -47,7 +51,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
47
51
|
|
|
48
52
|
switch (plane) {
|
|
49
53
|
case 'grid':
|
|
50
|
-
this._visibleRange
|
|
54
|
+
this._registry.set(this._visibleRange, range);
|
|
51
55
|
cells = this.getMainGridCells(range);
|
|
52
56
|
break;
|
|
53
57
|
case 'frozenRowsStart':
|
|
@@ -102,7 +106,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
102
106
|
private createDataCell(
|
|
103
107
|
cells: DxGridPlaneCells,
|
|
104
108
|
obj: T,
|
|
105
|
-
field: FieldType,
|
|
109
|
+
field: View.FieldType,
|
|
106
110
|
colIndex: number,
|
|
107
111
|
displayIndex: number,
|
|
108
112
|
): void {
|
|
@@ -120,12 +124,12 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
switch (props.format) {
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
127
|
+
case Format.TypeFormat.Boolean:
|
|
128
|
+
case Format.TypeFormat.SingleSelect:
|
|
129
|
+
case Format.TypeFormat.MultiSelect: {
|
|
126
130
|
return '';
|
|
127
131
|
}
|
|
128
|
-
case
|
|
132
|
+
case Format.TypeFormat.Ref: {
|
|
129
133
|
if (!field.referencePath) {
|
|
130
134
|
return ''; // TODO(burdon): Show error.
|
|
131
135
|
}
|
|
@@ -201,16 +205,16 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
201
205
|
}
|
|
202
206
|
|
|
203
207
|
// References.
|
|
204
|
-
if (props.format ===
|
|
208
|
+
if (props.format === Format.TypeFormat.Ref && props.referenceSchema) {
|
|
205
209
|
const targetObj = getValue(obj, field.path)?.target;
|
|
206
210
|
if (targetObj) {
|
|
207
|
-
const
|
|
208
|
-
cell.accessoryHtml = `<div role="none" class="absolute
|
|
211
|
+
const uri = Obj.getURI(targetObj);
|
|
212
|
+
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor uri=${uri} 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
213
|
}
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
// Booleans.
|
|
213
|
-
if (props.format ===
|
|
217
|
+
if (props.format === Format.TypeFormat.Boolean) {
|
|
214
218
|
const value = getValue(obj, field.path);
|
|
215
219
|
cell.accessoryHtml = tableControls.switch.render({
|
|
216
220
|
colIndex,
|
|
@@ -221,7 +225,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
// Single-Selects.
|
|
224
|
-
if (props.format ===
|
|
228
|
+
if (props.format === Format.TypeFormat.SingleSelect) {
|
|
225
229
|
const value = getValue(obj, field.path);
|
|
226
230
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
227
231
|
if (options) {
|
|
@@ -233,7 +237,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
233
237
|
}
|
|
234
238
|
|
|
235
239
|
// Multi-Selects.
|
|
236
|
-
if (props.format ===
|
|
240
|
+
if (props.format === Format.TypeFormat.MultiSelect) {
|
|
237
241
|
const values = getValue(obj, field.path) as string[] | undefined;
|
|
238
242
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
239
243
|
if (options && values && values.length > 0) {
|
|
@@ -260,7 +264,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
260
264
|
|
|
261
265
|
private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
262
266
|
const cells: DxGridPlaneCells = {};
|
|
263
|
-
const fields = this.model.projection?.
|
|
267
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
264
268
|
|
|
265
269
|
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
266
270
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
@@ -269,7 +273,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
269
273
|
continue;
|
|
270
274
|
}
|
|
271
275
|
|
|
272
|
-
this.createDataCell(cells, this.model.
|
|
276
|
+
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
273
277
|
}
|
|
274
278
|
}
|
|
275
279
|
|
|
@@ -278,8 +282,8 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
278
282
|
|
|
279
283
|
private getDraftRowCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
280
284
|
const cells: DxGridPlaneCells = {};
|
|
281
|
-
const fields = this.model.projection?.
|
|
282
|
-
const draftRows = this.model.
|
|
285
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
286
|
+
const draftRows = this.model.getDraftRows();
|
|
283
287
|
|
|
284
288
|
// Return cells of the CTA row if no draft row is active
|
|
285
289
|
if (draftRows.length === 0) {
|
|
@@ -319,22 +323,22 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
319
323
|
|
|
320
324
|
private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
321
325
|
const cells: DxGridPlaneCells = {};
|
|
322
|
-
const fields = this.model.projection?.
|
|
326
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
327
|
+
const currentSort = this.model.getSorting();
|
|
323
328
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
324
329
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
325
|
-
const
|
|
326
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : undefined;
|
|
330
|
+
const direction = currentSort?.fieldId === field.id ? currentSort.direction : undefined;
|
|
327
331
|
|
|
328
332
|
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
329
333
|
// TODO(burdon): Use same logic as form for fallback title.
|
|
330
334
|
value: '',
|
|
331
335
|
resizeHandle: 'col',
|
|
332
336
|
accessoryHtml: `
|
|
333
|
-
<span class="grow min-
|
|
337
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
334
338
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
335
339
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
336
340
|
`,
|
|
337
|
-
className: '!bg-
|
|
341
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
338
342
|
};
|
|
339
343
|
}
|
|
340
344
|
|
|
@@ -377,7 +381,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
377
381
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
378
382
|
return {
|
|
379
383
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
380
|
-
className: '!bg-
|
|
384
|
+
className: '!bg-toolbar-surface',
|
|
381
385
|
readonly: true,
|
|
382
386
|
value: '',
|
|
383
387
|
},
|
|
@@ -389,9 +393,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
389
393
|
accessoryHtml: tableControls.checkbox.render({
|
|
390
394
|
rowIndex: 0,
|
|
391
395
|
header: true,
|
|
392
|
-
checked: this.model.selection.
|
|
396
|
+
checked: this.model.selection.allRowsSelected,
|
|
393
397
|
}),
|
|
394
|
-
className: '!bg-
|
|
398
|
+
className: '!bg-toolbar-surface',
|
|
395
399
|
readonly: true,
|
|
396
400
|
value: '',
|
|
397
401
|
},
|
|
@@ -403,10 +407,10 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
403
407
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
404
408
|
accessoryHtml: this.model.features.schemaEditable
|
|
405
409
|
? tableButtons.addColumn.render({
|
|
406
|
-
disabled: (this.model.projection?.
|
|
410
|
+
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
407
411
|
})
|
|
408
412
|
: undefined,
|
|
409
|
-
className: '!bg-
|
|
413
|
+
className: '!bg-toolbar-surface',
|
|
410
414
|
readonly: true,
|
|
411
415
|
value: '',
|
|
412
416
|
},
|
|
@@ -415,7 +419,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
415
419
|
|
|
416
420
|
private getDraftActionCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
417
421
|
const cells: DxGridPlaneCells = {};
|
|
418
|
-
const draftRows = this.model.
|
|
422
|
+
const draftRows = this.model.getDraftRows();
|
|
419
423
|
|
|
420
424
|
// Return cells of the CTA row if no draft row is active
|
|
421
425
|
if (draftRows.length === 0) {
|
|
@@ -442,7 +446,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
442
446
|
|
|
443
447
|
private getDraftIconCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
444
448
|
const cells: DxGridPlaneCells = {};
|
|
445
|
-
const draftRows = this.model.
|
|
449
|
+
const draftRows = this.model.getDraftRows();
|
|
446
450
|
|
|
447
451
|
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
448
452
|
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
@@ -460,7 +464,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
460
464
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
461
465
|
if (!selected) {
|
|
462
466
|
if (selectionMode === 'single') {
|
|
463
|
-
return ['
|
|
467
|
+
return ['cursor-pointer!'];
|
|
464
468
|
} else {
|
|
465
469
|
return undefined;
|
|
466
470
|
}
|
|
@@ -468,8 +472,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
468
472
|
|
|
469
473
|
switch (selectionMode) {
|
|
470
474
|
case 'single':
|
|
471
|
-
return ['
|
|
475
|
+
return ['dx-grid__cell--no-focus-unfurl bg-current-surface! hover:bg-hover-surface cursor-pointer!'];
|
|
472
476
|
case 'multiple':
|
|
473
|
-
return ['
|
|
477
|
+
return ['bg-grid-cell-selected!'];
|
|
474
478
|
}
|
|
475
479
|
};
|
|
@@ -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,12 +168,41 @@ 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
|
|
|
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
|
+
|
|
162
206
|
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
163
207
|
test.skip('extant relations work as expected', async ({ browser, browserName }) => {
|
|
164
208
|
test.skip(browserName === 'webkit');
|
|
@@ -266,7 +310,7 @@ test.describe('Table', () => {
|
|
|
266
310
|
dxGridElement.scrollToColumn(6);
|
|
267
311
|
});
|
|
268
312
|
|
|
269
|
-
const nonRefContent =
|
|
313
|
+
const nonRefContent = random.lorem.words(3);
|
|
270
314
|
const nonRefCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="6"]');
|
|
271
315
|
await nonRefCell.click();
|
|
272
316
|
await page.keyboard.press('Enter');
|