@dxos/react-ui-table 0.7.5-main.499c70c → 0.7.5-main.937ce75
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/index.mjs +158 -117
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +146 -102
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +158 -117
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +6 -15
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +48 -0
- package/dist/types/src/model/table-sorting.d.ts.map +1 -0
- package/package.json +29 -29
- package/src/components/Table/CreateRefPanel.tsx +3 -4
- package/src/model/table-model.ts +28 -86
- package/src/model/table-presentation.ts +5 -2
- package/src/model/table-sorting.ts +131 -0
- package/src/playwright/smoke.spec.ts +16 -1
package/src/model/table-model.ts
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { computed, effect, signal, type ReadonlySignal } from '@preact/signals-core';
|
|
6
|
-
import orderBy from 'lodash.orderby';
|
|
7
6
|
|
|
8
7
|
import { Resource } from '@dxos/context';
|
|
9
8
|
import { getValue, setValue, FormatEnum, type JsonProp } from '@dxos/echo-schema';
|
|
10
9
|
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { isReactiveObject, makeRef } from '@dxos/live-object';
|
|
12
11
|
import { PublicKey } from '@dxos/react-client';
|
|
13
|
-
import {
|
|
12
|
+
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
14
13
|
import {
|
|
15
14
|
type DxGridAxisMeta,
|
|
16
15
|
type DxGridPlaneRange,
|
|
@@ -20,12 +19,10 @@ import {
|
|
|
20
19
|
import { type ViewProjection } from '@dxos/schema';
|
|
21
20
|
|
|
22
21
|
import { SelectionModel } from './selection-model';
|
|
22
|
+
import { type SortConfig, type SortDirection, TableSorting } from './table-sorting';
|
|
23
23
|
import { type TableType } from '../types';
|
|
24
24
|
import { touch } from '../util';
|
|
25
25
|
|
|
26
|
-
export type SortDirection = 'asc' | 'desc';
|
|
27
|
-
export type SortConfig = { fieldId: string; direction: SortDirection };
|
|
28
|
-
|
|
29
26
|
export type BaseTableRow = Record<JsonProp, any> & { id: string };
|
|
30
27
|
|
|
31
28
|
export type TableModelProps<T extends BaseTableRow = { id: string }> = {
|
|
@@ -51,25 +48,16 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
51
48
|
end: { row: 0, col: 0 },
|
|
52
49
|
});
|
|
53
50
|
|
|
54
|
-
/**
|
|
55
|
-
* Maps display indices to data indices.
|
|
56
|
-
* Used for translating between sorted/displayed order and original data order.
|
|
57
|
-
* Keys are display indices, values are corresponding data indices.
|
|
58
|
-
*/
|
|
59
|
-
private readonly _displayToDataIndex: Map<number, number> = new Map();
|
|
60
|
-
|
|
61
51
|
private readonly _onInsertRow?: TableModelProps<T>['onInsertRow'];
|
|
62
52
|
private readonly _onDeleteRows?: TableModelProps<T>['onDeleteRows'];
|
|
63
53
|
private readonly _onDeleteColumn?: TableModelProps<T>['onDeleteColumn'];
|
|
64
54
|
private readonly _onCellUpdate?: TableModelProps<T>['onCellUpdate'];
|
|
65
55
|
private readonly _onRowOrderChanged?: TableModelProps<T>['onRowOrderChanged'];
|
|
66
56
|
|
|
67
|
-
private readonly
|
|
68
|
-
private
|
|
69
|
-
private _rows = signal<T[]>([]);
|
|
57
|
+
private readonly _rows = signal<T[]>([]);
|
|
58
|
+
private readonly _sorting: TableSorting<T>;
|
|
70
59
|
|
|
71
60
|
private _pinnedRows: NonNullable<TableModelProps<T>['pinnedRows']>;
|
|
72
|
-
|
|
73
61
|
private _selection!: SelectionModel<T>;
|
|
74
62
|
private _columnMeta?: ReadonlySignal<DxGridAxisMeta>;
|
|
75
63
|
|
|
@@ -87,8 +75,13 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
87
75
|
super();
|
|
88
76
|
this._table = table;
|
|
89
77
|
this._projection = projection;
|
|
78
|
+
this._sorting = new TableSorting(this._rows, table, projection);
|
|
79
|
+
|
|
80
|
+
if (sorting.length > 0) {
|
|
81
|
+
const [sort] = sorting;
|
|
82
|
+
this._sorting.setSort(sort.fieldId, sort.direction);
|
|
83
|
+
}
|
|
90
84
|
|
|
91
|
-
this._sorting.value = sorting.at(0);
|
|
92
85
|
this._pinnedRows = pinnedRows;
|
|
93
86
|
this._onInsertRow = onInsertRow;
|
|
94
87
|
this._onDeleteRows = onDeleteRows;
|
|
@@ -106,7 +99,14 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
public get rows(): ReadonlySignal<T[]> {
|
|
109
|
-
return this.
|
|
102
|
+
return this._sorting.sortedRows;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @reactive
|
|
107
|
+
*/
|
|
108
|
+
public get sorting(): SortConfig | undefined {
|
|
109
|
+
return this._sorting.sorting;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
public get pinnedRows(): NonNullable<TableModelProps<T>['pinnedRows']> {
|
|
@@ -118,10 +118,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
118
118
|
return this._columnMeta;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
public get sorting(): SortConfig | undefined {
|
|
122
|
-
return this._sorting.value;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
121
|
public get selection() {
|
|
126
122
|
return this._selection;
|
|
127
123
|
}
|
|
@@ -132,9 +128,8 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
132
128
|
|
|
133
129
|
protected override async _open() {
|
|
134
130
|
this.initializeColumnMeta();
|
|
135
|
-
this.initializeSorting();
|
|
136
131
|
this.initializeEffects();
|
|
137
|
-
this._selection = new SelectionModel(this.
|
|
132
|
+
this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
|
|
138
133
|
await this._selection.open(this._ctx);
|
|
139
134
|
}
|
|
140
135
|
|
|
@@ -153,59 +148,9 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
153
148
|
});
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
private initializeSorting(): void {
|
|
157
|
-
this._sortedRows = computed(() => {
|
|
158
|
-
this._displayToDataIndex.clear();
|
|
159
|
-
const sort = this._sorting.value;
|
|
160
|
-
if (!sort) {
|
|
161
|
-
return this._rows.value;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const field = this._table.view?.target?.fields.find((field) => field.id === sort.fieldId);
|
|
165
|
-
if (!field) {
|
|
166
|
-
return this._rows.value;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
170
|
-
|
|
171
|
-
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
172
|
-
const rawValue = getValue(item, field.path);
|
|
173
|
-
let sortValue = rawValue;
|
|
174
|
-
|
|
175
|
-
if (props.type.startsWith('boolean')) {
|
|
176
|
-
sortValue = !!rawValue; // Coerce null/undefined/false to false for booleans
|
|
177
|
-
} else if (rawValue != null) {
|
|
178
|
-
if (props.format === FormatEnum.Ref && field.referencePath) {
|
|
179
|
-
sortValue = getValue(rawValue, field.referencePath);
|
|
180
|
-
} else {
|
|
181
|
-
sortValue = formatForDisplay({ type: props.type, format: props.format, value: rawValue });
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
item,
|
|
187
|
-
index,
|
|
188
|
-
value: sortValue,
|
|
189
|
-
isEmpty: !props.type.startsWith('boolean') && (sortValue == null || sortValue === ''),
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const sorted = orderBy(dataWithIndices, ['isEmpty', 'value'], ['asc', sort.direction]);
|
|
194
|
-
|
|
195
|
-
for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
|
|
196
|
-
const { index: dataIndex } = sorted[displayIndex];
|
|
197
|
-
if (displayIndex !== dataIndex) {
|
|
198
|
-
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return sorted.map(({ item }) => item);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
151
|
private initializeEffects(): void {
|
|
207
152
|
const rowOrderWatcher = effect(() => {
|
|
208
|
-
touch(this.
|
|
153
|
+
touch(this._sorting.sortedRows.value);
|
|
209
154
|
this._onRowOrderChanged?.();
|
|
210
155
|
});
|
|
211
156
|
this._ctx.onDispose(rowOrderWatcher);
|
|
@@ -221,7 +166,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
221
166
|
for (let row = start.row; row <= end.row; row++) {
|
|
222
167
|
rowEffects.push(
|
|
223
168
|
effect(() => {
|
|
224
|
-
const obj = this.
|
|
169
|
+
const obj = this._sorting.sortedRows.value[row];
|
|
225
170
|
this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
|
|
226
171
|
this._onCellUpdate?.({ row, col: start.col, plane: 'grid' });
|
|
227
172
|
}),
|
|
@@ -246,12 +191,12 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
246
191
|
public getColumnCount = (): number => this.table.view?.target?.fields.length ?? 0;
|
|
247
192
|
|
|
248
193
|
public insertRow = (rowIndex?: number): void => {
|
|
249
|
-
const row = rowIndex !== undefined ? this.
|
|
194
|
+
const row = rowIndex !== undefined ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
|
|
250
195
|
this._onInsertRow?.(row);
|
|
251
196
|
};
|
|
252
197
|
|
|
253
198
|
public deleteRow = (rowIndex: number): void => {
|
|
254
|
-
const row = this.
|
|
199
|
+
const row = this._sorting.getDataIndex(rowIndex);
|
|
255
200
|
const obj = this._rows.value[row];
|
|
256
201
|
const objectsToDelete = [];
|
|
257
202
|
|
|
@@ -272,7 +217,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
272
217
|
}
|
|
273
218
|
|
|
274
219
|
const field = fields[col];
|
|
275
|
-
const dataIndex = this.
|
|
220
|
+
const dataIndex = this._sorting.getDataIndex(row);
|
|
276
221
|
const value = getValue(this._rows.value[dataIndex], field.path);
|
|
277
222
|
if (value == null) {
|
|
278
223
|
return '';
|
|
@@ -295,7 +240,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
295
240
|
};
|
|
296
241
|
|
|
297
242
|
public setCellData = ({ col, row }: DxGridPlanePosition, value: any): void => {
|
|
298
|
-
const rowIdx = this.
|
|
243
|
+
const rowIdx = this._sorting.getDataIndex(row);
|
|
299
244
|
const fields = this.table.view?.target?.fields ?? [];
|
|
300
245
|
if (col < 0 || col >= fields.length) {
|
|
301
246
|
return;
|
|
@@ -334,7 +279,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
334
279
|
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
335
280
|
*/
|
|
336
281
|
public updateCellData({ col, row }: DxGridPlanePosition, update: (value: any) => any): void {
|
|
337
|
-
const dataRow = this.
|
|
282
|
+
const dataRow = this._sorting.getDataIndex(row);
|
|
338
283
|
const fields = this.table.view?.target?.fields ?? [];
|
|
339
284
|
const field = fields[col];
|
|
340
285
|
|
|
@@ -354,9 +299,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
354
299
|
|
|
355
300
|
const field = this.table.view.target?.fields.find((field) => field.id === fieldId);
|
|
356
301
|
if (field && this._onDeleteColumn) {
|
|
357
|
-
if (this._sorting.value?.fieldId === fieldId) {
|
|
358
|
-
this.clearSort();
|
|
359
|
-
}
|
|
360
302
|
this._onDeleteColumn(field.id);
|
|
361
303
|
}
|
|
362
304
|
}
|
|
@@ -381,11 +323,11 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
381
323
|
//
|
|
382
324
|
|
|
383
325
|
public setSort(fieldId: string, direction: SortDirection): void {
|
|
384
|
-
this._sorting.
|
|
326
|
+
this._sorting.setSort(fieldId, direction);
|
|
385
327
|
}
|
|
386
328
|
|
|
387
329
|
public clearSort(): void {
|
|
388
|
-
this._sorting.
|
|
330
|
+
this._sorting.setSort('', 'asc');
|
|
389
331
|
}
|
|
390
332
|
|
|
391
333
|
//
|
|
@@ -148,6 +148,7 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
148
148
|
readonly: true,
|
|
149
149
|
resizeHandle: 'col',
|
|
150
150
|
accessoryHtml: tableButtons.columnSettings.render({ fieldId: field.id }),
|
|
151
|
+
className: '!bg-gridHeader',
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
|
|
@@ -189,13 +190,14 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
189
190
|
private getSelectAllCell(): DxGridPlaneCells {
|
|
190
191
|
return {
|
|
191
192
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
192
|
-
value: '',
|
|
193
193
|
accessoryHtml: tableControls.checkbox.render({
|
|
194
194
|
rowIndex: 0,
|
|
195
195
|
header: true,
|
|
196
196
|
checked: this.model.selection.allRowsSeleted.value,
|
|
197
197
|
}),
|
|
198
|
+
className: '!bg-gridHeader',
|
|
198
199
|
readonly: true,
|
|
200
|
+
value: '',
|
|
199
201
|
},
|
|
200
202
|
};
|
|
201
203
|
}
|
|
@@ -203,11 +205,12 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
203
205
|
private getNewColumnCell(): DxGridPlaneCells {
|
|
204
206
|
return {
|
|
205
207
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
206
|
-
value: '',
|
|
207
208
|
accessoryHtml: tableButtons.addColumn.render({
|
|
208
209
|
disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
209
210
|
}),
|
|
211
|
+
className: '!bg-gridHeader',
|
|
210
212
|
readonly: true,
|
|
213
|
+
value: '',
|
|
211
214
|
},
|
|
212
215
|
};
|
|
213
216
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { computed, type Signal, signal, type ReadonlySignal } from '@preact/signals-core';
|
|
6
|
+
import orderBy from 'lodash.orderby';
|
|
7
|
+
|
|
8
|
+
import { getValue, FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
9
|
+
import { formatForDisplay } from '@dxos/react-ui-form';
|
|
10
|
+
import { type PropertyType, type FieldType, type ViewProjection } from '@dxos/schema';
|
|
11
|
+
|
|
12
|
+
import { type BaseTableRow } from './table-model';
|
|
13
|
+
import { type TableType } from '../types';
|
|
14
|
+
|
|
15
|
+
export type SortDirection = 'asc' | 'desc';
|
|
16
|
+
export type SortConfig = { fieldId: string; direction: SortDirection };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Manages table sorting functionality through display and data index mapping.
|
|
20
|
+
*
|
|
21
|
+
* When a table is sorted, rows visually reorder but we need to maintain references
|
|
22
|
+
* to the original data positions. This is handled through two types of indices:
|
|
23
|
+
*
|
|
24
|
+
* - Display indices: The visual positions after sorting
|
|
25
|
+
* - Data indices: The original positions in the source data
|
|
26
|
+
*
|
|
27
|
+
* The _displayToDataIndex map maintains these relationships, enabling lookups
|
|
28
|
+
* between visual and source positions.
|
|
29
|
+
*
|
|
30
|
+
* Example mapping between display and data indices:
|
|
31
|
+
*
|
|
32
|
+
* Original Data: Sorted Display: Mapping: (Display → Data)
|
|
33
|
+
* ┌──────────────┐ ┌──────────────┐ ┌────────┐
|
|
34
|
+
* │ 0: "Banana" │ => │ 0: "Apple" │ => │ 0 => 1 │
|
|
35
|
+
* │ 1: "Apple" │ => │ 1: "Banana" │ => │ 1 => 0 │
|
|
36
|
+
* │ 2: "Cherry" │ => │ 2: "Cherry" │ => │ 2 => 2 │
|
|
37
|
+
* └──────────────┘ └──────────────┘ └────────┘
|
|
38
|
+
*/
|
|
39
|
+
export class TableSorting<T extends BaseTableRow> {
|
|
40
|
+
private readonly _displayToDataIndex = new Map<number, number>();
|
|
41
|
+
private readonly _sorting = signal<SortConfig | undefined>(undefined);
|
|
42
|
+
private readonly _rows: Signal<T[]>;
|
|
43
|
+
public readonly sortedRows: ReadonlySignal<T[]>;
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
rows: Signal<T[]>,
|
|
47
|
+
private readonly _table: TableType,
|
|
48
|
+
private readonly _projection: ViewProjection,
|
|
49
|
+
) {
|
|
50
|
+
this._rows = rows;
|
|
51
|
+
this.sortedRows = this.initialiseSortedRows();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @reactive
|
|
56
|
+
*/
|
|
57
|
+
public get sorting(): SortConfig | undefined {
|
|
58
|
+
return this._sorting.value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public getDataIndex(displayIndex: number): number {
|
|
62
|
+
return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public setSort(fieldId: string, direction: SortDirection): void {
|
|
66
|
+
this._sorting.value = { fieldId, direction };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//
|
|
70
|
+
// Initialisation.
|
|
71
|
+
//
|
|
72
|
+
|
|
73
|
+
private initialiseSortedRows(): ReadonlySignal<T[]> {
|
|
74
|
+
return computed(() => {
|
|
75
|
+
this._displayToDataIndex.clear();
|
|
76
|
+
const sort = this._sorting.value;
|
|
77
|
+
if (!sort) {
|
|
78
|
+
return this._rows.value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const field = this._table.view?.target?.fields.find((f) => f.id === sort.fieldId);
|
|
82
|
+
if (!field) {
|
|
83
|
+
return this._rows.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { props } = this._projection.getFieldProjection(field.id);
|
|
87
|
+
|
|
88
|
+
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
89
|
+
const sortValue = this.getSortValue(props, field, item);
|
|
90
|
+
const isEmpty = !(props.type === TypeEnum.Boolean) && (sortValue == null || sortValue === '');
|
|
91
|
+
|
|
92
|
+
return { item, index, value: sortValue, isEmpty };
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const ordered = orderBy(dataWithIndices, ['isEmpty', 'value'], ['asc', sort.direction]);
|
|
96
|
+
|
|
97
|
+
ordered.forEach(({ index: dataIndex }, displayIndex) => {
|
|
98
|
+
if (displayIndex !== dataIndex) {
|
|
99
|
+
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return ordered.map(({ item }) => item);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private getSortValue(props: PropertyType, field: FieldType, item: T) {
|
|
108
|
+
const { type, format } = props;
|
|
109
|
+
const value = getValue(item, field.path);
|
|
110
|
+
|
|
111
|
+
if (type === TypeEnum.Boolean) {
|
|
112
|
+
return !!value; // Coerce null/undefined to false for booleans.
|
|
113
|
+
}
|
|
114
|
+
if (type === TypeEnum.Number) {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
if (value == null) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// TODO(ZaymonFC): Should we format this returned value for display based on the referenced prop format?
|
|
122
|
+
// Right now we're going to be sorting based on the raw values.
|
|
123
|
+
// Maybe we need to recurse this function with the referenced prop format and field?
|
|
124
|
+
if (format === FormatEnum.Ref && field.referencePath) {
|
|
125
|
+
const refValue = getValue(value.target, field.referencePath);
|
|
126
|
+
return refValue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return formatForDisplay({ type, format, value });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -123,21 +123,36 @@ test.describe('Table', () => {
|
|
|
123
123
|
await page.close();
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
test('
|
|
126
|
+
test('reference > reference / create new object', async ({ browser }) => {
|
|
127
127
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
128
128
|
const table = new TableManager(page);
|
|
129
129
|
|
|
130
130
|
await table.grid.ready();
|
|
131
131
|
|
|
132
|
+
// Reference existing object.
|
|
133
|
+
// Scroll over to the manager column.
|
|
132
134
|
await table.grid.panByWheel(10000, 0);
|
|
133
135
|
|
|
134
136
|
await table.grid.cell(4, 0, 'grid').click();
|
|
135
137
|
await page.keyboard.press('A');
|
|
138
|
+
await page.keyboard.press('N');
|
|
136
139
|
await page.getByRole('option', { name: 'Anita Mayer' }).click();
|
|
137
140
|
|
|
138
141
|
// Assert that the value is shown in the cell.
|
|
139
142
|
await expect(page.getByRole('gridcell', { name: 'Anita Mayer' })).toBeVisible();
|
|
140
143
|
|
|
144
|
+
// Create new object.
|
|
145
|
+
await table.grid.cell(4, 1, 'grid').click();
|
|
146
|
+
await page.keyboard.type('t');
|
|
147
|
+
await page.keyboard.type('e');
|
|
148
|
+
await page.keyboard.type('s');
|
|
149
|
+
await page.keyboard.type('t');
|
|
150
|
+
await page.getByText('Create new object', { exact: false }).click();
|
|
151
|
+
await page.getByTestId('save-button').click();
|
|
152
|
+
|
|
153
|
+
// Scroll to the left.
|
|
154
|
+
await page.getByRole('gridcell', { name: 'test' }).click();
|
|
155
|
+
|
|
141
156
|
await page.close();
|
|
142
157
|
});
|
|
143
158
|
|