@dxos/react-ui-table 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae
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-DSR2MRZB.mjs +80 -0
- package/dist/lib/browser/chunk-DSR2MRZB.mjs.map +7 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs +1751 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1160 -1024
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -42
- 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-GI72MQSG.mjs +1753 -0
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HTZJSULI.mjs +81 -0
- package/dist/lib/node-esm/chunk-HTZJSULI.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1160 -1024
- 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 +30 -42
- 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 +7 -7
- 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 +12 -12
- 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 +5 -5
- 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 +9 -9
- 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 +105 -25
- 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/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 +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/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 +54 -54
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +16 -16
- package/src/components/Table/DynamicTable.tsx +29 -21
- package/src/components/Table/Relations.stories.tsx +52 -60
- package/src/components/Table/RowActionsMenu.tsx +24 -15
- package/src/components/Table/Table.stories.tsx +114 -96
- package/src/components/Table/Table.tsx +326 -300
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -5
- package/src/components/TableCellEditor/FormCellEditor.tsx +117 -134
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +18 -13
- package/src/components/TableCellEditor/TableCellEditor.tsx +31 -31
- package/src/components/TableToolbar/TableToolbar.tsx +53 -37
- 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 +36 -34
- 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 +436 -170
- package/src/model/table-presentation.test.ts +59 -49
- package/src/model/table-presentation.ts +43 -37
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +56 -10
- package/src/testing/useTestTableModel.ts +45 -38
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +95 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +41 -28
- 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-4K7JPALT.mjs +0 -66
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZTXLZGM.mjs +0 -1804
- package/dist/lib/browser/chunk-OZTXLZGM.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/lib/node-esm/chunk-S56Z4V4X.mjs +0 -1805
- package/dist/lib/node-esm/chunk-S56Z4V4X.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, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import {
|
|
12
|
-
import { createView } from '@dxos/schema';
|
|
11
|
+
import { ProjectionModel, ViewModel, 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 = ViewModel.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, JsonSchema.toJsonSchema(schema)),
|
|
112
|
+
});
|
|
113
|
+
projection.normalizeView();
|
|
114
|
+
return new TableModel({ registry, object, projection, ...props });
|
|
105
115
|
};
|
|
@@ -2,11 +2,12 @@
|
|
|
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 { 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,8 +16,8 @@ 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
|
|
|
@@ -29,15 +30,20 @@ import { type TableModel, type TableRow } from './table-model';
|
|
|
29
30
|
* different grid planes.
|
|
30
31
|
*/
|
|
31
32
|
export class TablePresentation<T extends TableRow = TableRow> {
|
|
33
|
+
private readonly _registry: Registry.Registry;
|
|
34
|
+
private readonly _visibleRange: Atom.Writable<DxGridPlaneRange>;
|
|
32
35
|
private fieldProjectionCache = new Map<string, ReturnType<typeof this.model.projection.getFieldProjection>>();
|
|
33
36
|
|
|
34
37
|
constructor(
|
|
38
|
+
registry: Registry.Registry,
|
|
35
39
|
private readonly model: TableModel<T>,
|
|
36
|
-
|
|
40
|
+
) {
|
|
41
|
+
this._registry = registry;
|
|
42
|
+
this._visibleRange = Atom.make<DxGridPlaneRange>({
|
|
37
43
|
start: { row: 0, col: 0 },
|
|
38
44
|
end: { row: 0, col: 0 },
|
|
39
|
-
})
|
|
40
|
-
|
|
45
|
+
});
|
|
46
|
+
}
|
|
41
47
|
|
|
42
48
|
public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
|
|
43
49
|
// Clear cache at the start of each render pass
|
|
@@ -47,7 +53,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
47
53
|
|
|
48
54
|
switch (plane) {
|
|
49
55
|
case 'grid':
|
|
50
|
-
this._visibleRange
|
|
56
|
+
this._registry.set(this._visibleRange, range);
|
|
51
57
|
cells = this.getMainGridCells(range);
|
|
52
58
|
break;
|
|
53
59
|
case 'frozenRowsStart':
|
|
@@ -102,7 +108,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
102
108
|
private createDataCell(
|
|
103
109
|
cells: DxGridPlaneCells,
|
|
104
110
|
obj: T,
|
|
105
|
-
field: FieldType,
|
|
111
|
+
field: View.FieldType,
|
|
106
112
|
colIndex: number,
|
|
107
113
|
displayIndex: number,
|
|
108
114
|
): void {
|
|
@@ -120,12 +126,12 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
120
126
|
}
|
|
121
127
|
|
|
122
128
|
switch (props.format) {
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
129
|
+
case Format.TypeFormat.Boolean:
|
|
130
|
+
case Format.TypeFormat.SingleSelect:
|
|
131
|
+
case Format.TypeFormat.MultiSelect: {
|
|
126
132
|
return '';
|
|
127
133
|
}
|
|
128
|
-
case
|
|
134
|
+
case Format.TypeFormat.Ref: {
|
|
129
135
|
if (!field.referencePath) {
|
|
130
136
|
return ''; // TODO(burdon): Show error.
|
|
131
137
|
}
|
|
@@ -201,16 +207,16 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
// References.
|
|
204
|
-
if (props.format ===
|
|
210
|
+
if (props.format === Format.TypeFormat.Ref && props.referenceSchema) {
|
|
205
211
|
const targetObj = getValue(obj, field.path)?.target;
|
|
206
212
|
if (targetObj) {
|
|
207
213
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
208
|
-
cell.accessoryHtml = `<div role="none" class="absolute
|
|
214
|
+
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
215
|
}
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
// Booleans.
|
|
213
|
-
if (props.format ===
|
|
219
|
+
if (props.format === Format.TypeFormat.Boolean) {
|
|
214
220
|
const value = getValue(obj, field.path);
|
|
215
221
|
cell.accessoryHtml = tableControls.switch.render({
|
|
216
222
|
colIndex,
|
|
@@ -221,7 +227,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
221
227
|
}
|
|
222
228
|
|
|
223
229
|
// Single-Selects.
|
|
224
|
-
if (props.format ===
|
|
230
|
+
if (props.format === Format.TypeFormat.SingleSelect) {
|
|
225
231
|
const value = getValue(obj, field.path);
|
|
226
232
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
227
233
|
if (options) {
|
|
@@ -233,7 +239,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
// Multi-Selects.
|
|
236
|
-
if (props.format ===
|
|
242
|
+
if (props.format === Format.TypeFormat.MultiSelect) {
|
|
237
243
|
const values = getValue(obj, field.path) as string[] | undefined;
|
|
238
244
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
239
245
|
if (options && values && values.length > 0) {
|
|
@@ -260,7 +266,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
260
266
|
|
|
261
267
|
private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
262
268
|
const cells: DxGridPlaneCells = {};
|
|
263
|
-
const fields = this.model.projection?.
|
|
269
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
264
270
|
|
|
265
271
|
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
266
272
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
@@ -269,7 +275,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
269
275
|
continue;
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
this.createDataCell(cells, this.model.
|
|
278
|
+
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
273
279
|
}
|
|
274
280
|
}
|
|
275
281
|
|
|
@@ -278,8 +284,8 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
278
284
|
|
|
279
285
|
private getDraftRowCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
280
286
|
const cells: DxGridPlaneCells = {};
|
|
281
|
-
const fields = this.model.projection?.
|
|
282
|
-
const draftRows = this.model.
|
|
287
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
288
|
+
const draftRows = this.model.getDraftRows();
|
|
283
289
|
|
|
284
290
|
// Return cells of the CTA row if no draft row is active
|
|
285
291
|
if (draftRows.length === 0) {
|
|
@@ -319,22 +325,22 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
319
325
|
|
|
320
326
|
private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
321
327
|
const cells: DxGridPlaneCells = {};
|
|
322
|
-
const fields = this.model.projection?.
|
|
328
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
329
|
+
const currentSort = this.model.getSorting();
|
|
323
330
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
324
331
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
325
|
-
const
|
|
326
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : undefined;
|
|
332
|
+
const direction = currentSort?.fieldId === field.id ? currentSort.direction : undefined;
|
|
327
333
|
|
|
328
334
|
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
329
335
|
// TODO(burdon): Use same logic as form for fallback title.
|
|
330
336
|
value: '',
|
|
331
337
|
resizeHandle: 'col',
|
|
332
338
|
accessoryHtml: `
|
|
333
|
-
<span class="grow min-
|
|
339
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
334
340
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
335
341
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
336
342
|
`,
|
|
337
|
-
className: '!bg-
|
|
343
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
338
344
|
};
|
|
339
345
|
}
|
|
340
346
|
|
|
@@ -377,7 +383,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
377
383
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
378
384
|
return {
|
|
379
385
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
380
|
-
className: '!bg-
|
|
386
|
+
className: '!bg-toolbar-surface',
|
|
381
387
|
readonly: true,
|
|
382
388
|
value: '',
|
|
383
389
|
},
|
|
@@ -389,9 +395,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
389
395
|
accessoryHtml: tableControls.checkbox.render({
|
|
390
396
|
rowIndex: 0,
|
|
391
397
|
header: true,
|
|
392
|
-
checked: this.model.selection.
|
|
398
|
+
checked: this.model.selection.allRowsSelected,
|
|
393
399
|
}),
|
|
394
|
-
className: '!bg-
|
|
400
|
+
className: '!bg-toolbar-surface',
|
|
395
401
|
readonly: true,
|
|
396
402
|
value: '',
|
|
397
403
|
},
|
|
@@ -403,10 +409,10 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
403
409
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
404
410
|
accessoryHtml: this.model.features.schemaEditable
|
|
405
411
|
? tableButtons.addColumn.render({
|
|
406
|
-
disabled: (this.model.projection?.
|
|
412
|
+
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
407
413
|
})
|
|
408
414
|
: undefined,
|
|
409
|
-
className: '!bg-
|
|
415
|
+
className: '!bg-toolbar-surface',
|
|
410
416
|
readonly: true,
|
|
411
417
|
value: '',
|
|
412
418
|
},
|
|
@@ -415,7 +421,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
415
421
|
|
|
416
422
|
private getDraftActionCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
417
423
|
const cells: DxGridPlaneCells = {};
|
|
418
|
-
const draftRows = this.model.
|
|
424
|
+
const draftRows = this.model.getDraftRows();
|
|
419
425
|
|
|
420
426
|
// Return cells of the CTA row if no draft row is active
|
|
421
427
|
if (draftRows.length === 0) {
|
|
@@ -442,7 +448,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
442
448
|
|
|
443
449
|
private getDraftIconCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
444
450
|
const cells: DxGridPlaneCells = {};
|
|
445
|
-
const draftRows = this.model.
|
|
451
|
+
const draftRows = this.model.getDraftRows();
|
|
446
452
|
|
|
447
453
|
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
448
454
|
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
@@ -460,7 +466,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
460
466
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
461
467
|
if (!selected) {
|
|
462
468
|
if (selectionMode === 'single') {
|
|
463
|
-
return ['
|
|
469
|
+
return ['cursor-pointer!'];
|
|
464
470
|
} else {
|
|
465
471
|
return undefined;
|
|
466
472
|
}
|
|
@@ -468,8 +474,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
468
474
|
|
|
469
475
|
switch (selectionMode) {
|
|
470
476
|
case 'single':
|
|
471
|
-
return ['
|
|
477
|
+
return ['dx-grid__cell--no-focus-unfurl !bg-current-surface hover:bg-hover-surface cursor-pointer!'];
|
|
472
478
|
case 'multiple':
|
|
473
|
-
return ['!bg-
|
|
479
|
+
return ['!bg-grid-cell-selected'];
|
|
474
480
|
}
|
|
475
481
|
};
|
|
@@ -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
|
},
|
|
@@ -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);
|