@dxos/react-ui-table 0.7.1 → 0.7.2-staging.6d26b2a
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-A5W4XWPD.mjs → chunk-EWGUNGJC.mjs} +8 -6
- package/dist/lib/browser/chunk-EWGUNGJC.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +22 -11
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-32IGMRME.cjs → chunk-A2N7XWW6.cjs} +11 -9
- package/dist/lib/node/chunk-A2N7XWW6.cjs.map +7 -0
- package/dist/lib/node/index.cjs +23 -12
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +2 -2
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-DWQ4KGLD.mjs → chunk-SABDOZ6I.mjs} +8 -6
- package/dist/lib/node-esm/chunk-SABDOZ6I.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +22 -11
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -2
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +17 -0
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -0
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/playwright/smoke.spec.d.ts +2 -0
- package/dist/types/src/playwright/smoke.spec.d.ts.map +1 -0
- package/dist/types/src/types/table.d.ts +9 -7
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/grid.d.ts +3 -3
- package/dist/types/src/util/grid.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +5 -2
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/package.json +38 -39
- package/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.tsx +1 -1
- package/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.tsx +1 -1
- package/src/_deprecated/components/Table/Table.stories.tsx +1 -1
- package/src/components/Table/ColumnActionsMenu.tsx +11 -5
- package/src/components/Table/RowActionsMenu.tsx +1 -1
- package/src/components/Table/Table.stories.tsx +5 -3
- package/src/components/Table/Table.tsx +2 -2
- package/src/playwright/TableManager.ts +60 -0
- package/src/playwright/playwright.config.ts +18 -0
- package/src/playwright/smoke.spec.ts +105 -0
- package/src/types/table.ts +12 -3
- package/src/util/grid.ts +3 -3
- package/src/util/table-buttons.ts +9 -4
- package/src/util/table-controls.ts +1 -1
- package/src/util/table.ts +11 -3
- package/dist/lib/browser/chunk-A5W4XWPD.mjs.map +0 -7
- package/dist/lib/node/chunk-32IGMRME.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-DWQ4KGLD.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-table",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2-staging.6d26b2a",
|
|
4
4
|
"description": "A table component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -8,32 +8,30 @@
|
|
|
8
8
|
"author": "DXOS.org",
|
|
9
9
|
"sideEffects": true,
|
|
10
10
|
"exports": {
|
|
11
|
+
"./deprecated": {
|
|
12
|
+
"types": "./dist/types/src/_deprecated/index.d.ts",
|
|
13
|
+
"browser": "./dist/lib/browser/_deprecated/index.mjs",
|
|
14
|
+
"node": "./dist/lib/node-esm/_deprecated/index.mjs"
|
|
15
|
+
},
|
|
11
16
|
".": {
|
|
12
17
|
"types": "./dist/types/src/index.d.ts",
|
|
13
18
|
"browser": "./dist/lib/browser/index.mjs",
|
|
14
|
-
"
|
|
15
|
-
"import": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"node": "./dist/lib/node-esm/index.mjs"
|
|
16
20
|
},
|
|
17
21
|
"./types": {
|
|
18
22
|
"types": "./dist/types/src/types/index.d.ts",
|
|
19
23
|
"browser": "./dist/lib/browser/types/index.mjs",
|
|
20
|
-
"
|
|
21
|
-
"import": "./dist/lib/node-esm/types/index.mjs"
|
|
22
|
-
},
|
|
23
|
-
"./deprecated": {
|
|
24
|
-
"types": "./dist/types/src/_deprecated/index.d.ts",
|
|
25
|
-
"browser": "./dist/lib/browser/_deprecated/index.mjs",
|
|
26
|
-
"require": "./dist/lib/node/_deprecated/index.cjs",
|
|
27
|
-
"import": "./dist/lib/node-esm/_deprecated/index.mjs"
|
|
24
|
+
"node": "./dist/lib/node-esm/types/index.mjs"
|
|
28
25
|
}
|
|
29
26
|
},
|
|
27
|
+
"types": "dist/types/src/index.d.ts",
|
|
30
28
|
"typesVersions": {
|
|
31
29
|
"*": {
|
|
32
30
|
"deprecated": [
|
|
33
|
-
"
|
|
31
|
+
"dist/types/src/_deprecated/index.d.ts"
|
|
34
32
|
],
|
|
35
33
|
"types": [
|
|
36
|
-
"
|
|
34
|
+
"dist/types/src/types/index.d.ts"
|
|
37
35
|
]
|
|
38
36
|
}
|
|
39
37
|
},
|
|
@@ -55,25 +53,25 @@
|
|
|
55
53
|
"@tanstack/react-virtual": "^3.9.0",
|
|
56
54
|
"date-fns": "^3.3.1",
|
|
57
55
|
"lodash.orderby": "^4.6.0",
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/invariant": "0.7.
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/lit-grid": "0.7.
|
|
64
|
-
"@dxos/log": "0.7.
|
|
65
|
-
"@dxos/plugin-search": "0.7.
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/react-ui-
|
|
69
|
-
"@dxos/react-ui-
|
|
70
|
-
"@dxos/react-ui-
|
|
71
|
-
"@dxos/react-ui-
|
|
72
|
-
"@dxos/react-ui-
|
|
73
|
-
"@dxos/react-ui-
|
|
74
|
-
"@dxos/react-ui-types": "0.7.
|
|
75
|
-
"@dxos/schema": "0.7.
|
|
76
|
-
"@dxos/util": "0.7.
|
|
56
|
+
"@dxos/context": "0.7.2-staging.6d26b2a",
|
|
57
|
+
"@dxos/debug": "0.7.2-staging.6d26b2a",
|
|
58
|
+
"@dxos/echo-schema": "0.7.2-staging.6d26b2a",
|
|
59
|
+
"@dxos/invariant": "0.7.2-staging.6d26b2a",
|
|
60
|
+
"@dxos/keys": "0.7.2-staging.6d26b2a",
|
|
61
|
+
"@dxos/lit-grid": "0.7.2-staging.6d26b2a",
|
|
62
|
+
"@dxos/log": "0.7.2-staging.6d26b2a",
|
|
63
|
+
"@dxos/plugin-search": "0.7.2-staging.6d26b2a",
|
|
64
|
+
"@dxos/plugin-space": "0.7.2-staging.6d26b2a",
|
|
65
|
+
"@dxos/react-client": "0.7.2-staging.6d26b2a",
|
|
66
|
+
"@dxos/react-ui-form": "0.7.2-staging.6d26b2a",
|
|
67
|
+
"@dxos/react-ui-attention": "0.7.2-staging.6d26b2a",
|
|
68
|
+
"@dxos/react-ui-grid": "0.7.2-staging.6d26b2a",
|
|
69
|
+
"@dxos/react-ui-searchlist": "0.7.2-staging.6d26b2a",
|
|
70
|
+
"@dxos/react-ui-stack": "0.7.2-staging.6d26b2a",
|
|
71
|
+
"@dxos/react-ui-syntax-highlighter": "0.7.2-staging.6d26b2a",
|
|
72
|
+
"@dxos/react-ui-types": "0.7.2-staging.6d26b2a",
|
|
73
|
+
"@dxos/schema": "0.7.2-staging.6d26b2a",
|
|
74
|
+
"@dxos/util": "0.7.2-staging.6d26b2a"
|
|
77
75
|
},
|
|
78
76
|
"devDependencies": {
|
|
79
77
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -84,18 +82,19 @@
|
|
|
84
82
|
"react": "~18.2.0",
|
|
85
83
|
"react-dom": "~18.2.0",
|
|
86
84
|
"vite": "5.4.7",
|
|
87
|
-
"@dxos/
|
|
88
|
-
"@dxos/
|
|
89
|
-
"@dxos/react-ui": "0.7.
|
|
90
|
-
"@dxos/react-ui
|
|
91
|
-
"@dxos/
|
|
85
|
+
"@dxos/random": "0.7.2-staging.6d26b2a",
|
|
86
|
+
"@dxos/echo-signals": "0.7.2-staging.6d26b2a",
|
|
87
|
+
"@dxos/react-ui-theme": "0.7.2-staging.6d26b2a",
|
|
88
|
+
"@dxos/react-ui": "0.7.2-staging.6d26b2a",
|
|
89
|
+
"@dxos/test-utils": "0.7.2-staging.6d26b2a",
|
|
90
|
+
"@dxos/storybook-utils": "0.7.2-staging.6d26b2a"
|
|
92
91
|
},
|
|
93
92
|
"peerDependencies": {
|
|
94
93
|
"@phosphor-icons/react": "^2.1.5",
|
|
95
94
|
"react": "~18.2.0",
|
|
96
95
|
"react-dom": "~18.2.0",
|
|
97
|
-
"@dxos/react-ui": "0.7.
|
|
98
|
-
"@dxos/react-ui-theme": "0.7.
|
|
96
|
+
"@dxos/react-ui": "0.7.2-staging.6d26b2a",
|
|
97
|
+
"@dxos/react-ui-theme": "0.7.2-staging.6d26b2a"
|
|
99
98
|
},
|
|
100
99
|
"publishConfig": {
|
|
101
100
|
"access": "public"
|
|
@@ -15,7 +15,7 @@ import { type ColumnSettingsProps } from './ColumnSettings';
|
|
|
15
15
|
import { type ColumnDef } from '../../schema';
|
|
16
16
|
|
|
17
17
|
export default {
|
|
18
|
-
title: 'ui/react-ui-table/ColumnMenu',
|
|
18
|
+
title: 'ui/react-ui-table/deprecated/ColumnMenu',
|
|
19
19
|
component: ColumnMenu,
|
|
20
20
|
decorators: [withTheme],
|
|
21
21
|
};
|
|
@@ -20,7 +20,7 @@ const DefaultStory = (props: ColumnSettingsProps) => (
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
export default {
|
|
23
|
-
title: 'ui/react-ui-table/ColumnSettings',
|
|
23
|
+
title: 'ui/react-ui-table/deprecated/ColumnSettings',
|
|
24
24
|
component: ColumnSettings,
|
|
25
25
|
render: DefaultStory,
|
|
26
26
|
decorators: [withTheme, withLayout()],
|
|
@@ -29,26 +29,32 @@ export const ColumnActionsMenu = ({ model }: ColumnActionsMenuProps) => {
|
|
|
29
29
|
<DropdownMenu.Content>
|
|
30
30
|
<DropdownMenu.Viewport>
|
|
31
31
|
{(!isCurrentColumnSorted || currentSort?.direction === 'asc') && (
|
|
32
|
-
<DropdownMenu.Item
|
|
32
|
+
<DropdownMenu.Item
|
|
33
|
+
data-testid='column-sort-descending'
|
|
34
|
+
onClick={() => model.setSort(state.fieldId, 'desc')}
|
|
35
|
+
>
|
|
33
36
|
{t('column action sort descending')}
|
|
34
37
|
</DropdownMenu.Item>
|
|
35
38
|
)}
|
|
36
39
|
{(!isCurrentColumnSorted || currentSort?.direction === 'desc') && (
|
|
37
|
-
<DropdownMenu.Item
|
|
40
|
+
<DropdownMenu.Item
|
|
41
|
+
data-testid='column-sort-ascending'
|
|
42
|
+
onClick={() => model.setSort(state.fieldId, 'asc')}
|
|
43
|
+
>
|
|
38
44
|
{t('column action sort ascending')}
|
|
39
45
|
</DropdownMenu.Item>
|
|
40
46
|
)}
|
|
41
47
|
{isCurrentColumnSorted && (
|
|
42
|
-
<DropdownMenu.Item onClick={() => model.clearSort()}>
|
|
48
|
+
<DropdownMenu.Item data-testid='column-clear-sort' onClick={() => model.clearSort()}>
|
|
43
49
|
{t('column action clear sorting')}
|
|
44
50
|
</DropdownMenu.Item>
|
|
45
51
|
)}
|
|
46
52
|
{model.getColumnCount() > 1 && (
|
|
47
|
-
<DropdownMenu.Item onClick={() => model.deleteColumn(state.fieldId)}>
|
|
53
|
+
<DropdownMenu.Item data-testid='column-delete' onClick={() => model.deleteColumn(state.fieldId)}>
|
|
48
54
|
{t('column action delete')}
|
|
49
55
|
</DropdownMenu.Item>
|
|
50
56
|
)}
|
|
51
|
-
<DropdownMenu.Item onClick={() => model.modalController.openColumnSettings()}>
|
|
57
|
+
<DropdownMenu.Item data-testid='column-settings' onClick={() => model.modalController.openColumnSettings()}>
|
|
52
58
|
{t('column action settings')}
|
|
53
59
|
</DropdownMenu.Item>
|
|
54
60
|
</DropdownMenu.Viewport>
|
|
@@ -25,7 +25,7 @@ export const RowActionsMenu = ({ model }: RowActionsMenuProps) => {
|
|
|
25
25
|
<DropdownMenu.VirtualTrigger virtualRef={model.modalController.trigger} />
|
|
26
26
|
<DropdownMenu.Content>
|
|
27
27
|
<DropdownMenu.Viewport>
|
|
28
|
-
<DropdownMenu.Item onClick={() => model.deleteRow(state.rowIndex)}>
|
|
28
|
+
<DropdownMenu.Item data-testid='row-menu-delete' onClick={() => model.deleteRow(state.rowIndex)}>
|
|
29
29
|
{t(hasSelection ? 'bulk delete row label' : 'delete row label')}
|
|
30
30
|
</DropdownMenu.Item>
|
|
31
31
|
</DropdownMenu.Viewport>
|
|
@@ -27,6 +27,8 @@ import { initializeTable } from '../../util';
|
|
|
27
27
|
import { Toolbar } from '../Toolbar';
|
|
28
28
|
import { createItems, createTable, type SimulatorProps, useSimulator } from '../testing';
|
|
29
29
|
|
|
30
|
+
faker.seed(0);
|
|
31
|
+
|
|
30
32
|
//
|
|
31
33
|
// Story components.
|
|
32
34
|
//
|
|
@@ -189,7 +191,7 @@ const TablePerformanceStory = (props: StoryProps) => {
|
|
|
189
191
|
//
|
|
190
192
|
|
|
191
193
|
const meta: Meta<StoryProps> = {
|
|
192
|
-
title: '
|
|
194
|
+
title: 'ui/react-ui-table/Table',
|
|
193
195
|
component: Table.Main as any,
|
|
194
196
|
render: DefaultStory,
|
|
195
197
|
parameters: { translations },
|
|
@@ -200,8 +202,8 @@ const meta: Meta<StoryProps> = {
|
|
|
200
202
|
createSpace: true,
|
|
201
203
|
onSpaceCreated: async ({ space }) => {
|
|
202
204
|
const table = space.db.add(create(TableType, {}));
|
|
203
|
-
const schema = initializeTable({ space, table });
|
|
204
|
-
Array.from({ length:
|
|
205
|
+
const schema = initializeTable({ space, table, initialRow: false });
|
|
206
|
+
Array.from({ length: 10 }).map(() => {
|
|
205
207
|
return space.db.add(
|
|
206
208
|
create(schema, {
|
|
207
209
|
name: faker.person.fullName(),
|
|
@@ -48,8 +48,8 @@ const TableRoot = ({ children, role }: TableRootProps) => {
|
|
|
48
48
|
className={mx(
|
|
49
49
|
'relative border-bs !border-separator',
|
|
50
50
|
role === 'section'
|
|
51
|
-
? 'attention-surface overflow-hidden [&_.dx-grid]:max-is-
|
|
52
|
-
: 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-
|
|
51
|
+
? 'attention-surface overflow-hidden [&_.dx-grid]:max-is-[--dx-grid-content-inline-size]'
|
|
52
|
+
: 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
|
|
53
53
|
)}
|
|
54
54
|
>
|
|
55
55
|
{children}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Page } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
import { DxGridManager } from '@dxos/lit-grid/testing';
|
|
8
|
+
|
|
9
|
+
const TABLE_SELECTORS = {
|
|
10
|
+
columnSettingsButton: 'table-column-settings-button',
|
|
11
|
+
rowMenuButton: 'table-row-menu-button',
|
|
12
|
+
newColumnButton: 'table-new-column-button',
|
|
13
|
+
selection: 'table-selection',
|
|
14
|
+
columnSort: {
|
|
15
|
+
ascending: 'column-sort-ascending',
|
|
16
|
+
descending: 'column-sort-descending',
|
|
17
|
+
},
|
|
18
|
+
columnDelete: 'column-delete',
|
|
19
|
+
rowDelete: 'row-menu-delete',
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
export class TableManager {
|
|
23
|
+
private readonly _grid: DxGridManager;
|
|
24
|
+
|
|
25
|
+
constructor(private readonly page: Page) {
|
|
26
|
+
this._grid = new DxGridManager(page);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public get grid(): DxGridManager {
|
|
30
|
+
return this._grid;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async deleteColumn(index: number): Promise<void> {
|
|
34
|
+
await this.page.getByTestId(TABLE_SELECTORS.columnSettingsButton).nth(index).click();
|
|
35
|
+
await this.page.getByTestId(TABLE_SELECTORS.columnDelete).click();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async deleteRow(index = 0): Promise<void> {
|
|
39
|
+
await this.page.getByTestId(TABLE_SELECTORS.rowMenuButton).nth(index).click();
|
|
40
|
+
await this.page.getByTestId(TABLE_SELECTORS.rowDelete).click();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async toggleSelectAll(): Promise<void> {
|
|
44
|
+
await this.page.getByTestId(TABLE_SELECTORS.selection).nth(0).click();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async sortColumn(index: number, direction: 'ascending' | 'descending'): Promise<void> {
|
|
48
|
+
await this.page.getByTestId(TABLE_SELECTORS.columnSettingsButton).nth(index).click();
|
|
49
|
+
await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public async addColumn({ label, format }: { label: string; format: 'number' }): Promise<void> {
|
|
53
|
+
await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
|
|
54
|
+
await this.page.getByRole('combobox').click();
|
|
55
|
+
await this.page.getByLabel(`format ${format}`).click();
|
|
56
|
+
await this.page.getByPlaceholder('Property label.').click();
|
|
57
|
+
await this.page.getByPlaceholder('Property label.').fill(label);
|
|
58
|
+
await this.page.getByRole('button', { name: 'button save' }).click();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { nxE2EPreset } from '@nx/playwright/preset';
|
|
6
|
+
import { defineConfig } from '@playwright/test';
|
|
7
|
+
|
|
8
|
+
import { e2ePreset } from '@dxos/test-utils/playwright';
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
...nxE2EPreset(__filename, { testDir: __dirname }),
|
|
12
|
+
...e2ePreset(__dirname),
|
|
13
|
+
webServer: {
|
|
14
|
+
command: 'pnpm -w nx storybook-e2e stories',
|
|
15
|
+
port: 9009,
|
|
16
|
+
reuseExistingServer: !process.env.CI,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect, test } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
import { setupPage } from '@dxos/test-utils/playwright';
|
|
8
|
+
|
|
9
|
+
import { TableManager } from './TableManager';
|
|
10
|
+
|
|
11
|
+
const storyId = 'ui-react-ui-table-table';
|
|
12
|
+
const storyUrl = `http://localhost:9009/iframe.html?id=${storyId}&viewMode=story`;
|
|
13
|
+
|
|
14
|
+
// NOTE(ZaymonFC): This test suite relies on the faker seed being set to 0 in the story.
|
|
15
|
+
test.describe('Table', () => {
|
|
16
|
+
test('Loads', async ({ browser }) => {
|
|
17
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
18
|
+
const table = new TableManager(page);
|
|
19
|
+
|
|
20
|
+
// (name, email, salary, manager) * 10 rows.
|
|
21
|
+
await table.grid.ready();
|
|
22
|
+
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(40);
|
|
23
|
+
await page.close();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('sort', async ({ browser }) => {
|
|
27
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
28
|
+
const table = new TableManager(page);
|
|
29
|
+
|
|
30
|
+
await table.grid.ready();
|
|
31
|
+
await table.sortColumn(0, 'descending');
|
|
32
|
+
await expect(table.grid.cell(0, 0, 'grid')).toHaveText('Uwe Øvergård');
|
|
33
|
+
await expect(table.grid.cell(0, 9, 'grid')).toContainText('Anita');
|
|
34
|
+
|
|
35
|
+
await table.sortColumn(0, 'ascending');
|
|
36
|
+
await expect(table.grid.cell(0, 0, 'grid')).toContainText('Anita');
|
|
37
|
+
await expect(table.grid.cell(0, 9, 'grid')).toHaveText('Uwe Øvergård');
|
|
38
|
+
await page.close();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('delete row', async ({ browser }) => {
|
|
42
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
43
|
+
const table = new TableManager(page);
|
|
44
|
+
|
|
45
|
+
await table.grid.ready();
|
|
46
|
+
await table.deleteRow(0);
|
|
47
|
+
await expect(page.getByRole('gridcell', { name: 'Anita Mayer' })).toHaveCount(0);
|
|
48
|
+
await page.close();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('delete row--select all', async ({ browser }) => {
|
|
52
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
53
|
+
const table = new TableManager(page);
|
|
54
|
+
|
|
55
|
+
await table.grid.ready();
|
|
56
|
+
await table.toggleSelectAll();
|
|
57
|
+
|
|
58
|
+
// Delete action affects all selected rows.
|
|
59
|
+
await table.deleteRow(0);
|
|
60
|
+
|
|
61
|
+
await expect(page.getByRole('gridcell', { name: 'Anita Mayer' })).toHaveCount(0);
|
|
62
|
+
await expect(page.getByRole('gridcell', { name: 'Phonthip Sigurjónsson' })).toHaveCount(0);
|
|
63
|
+
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(0);
|
|
64
|
+
await page.close();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('delete column', async ({ browser }) => {
|
|
68
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
69
|
+
const table = new TableManager(page);
|
|
70
|
+
|
|
71
|
+
await table.grid.ready();
|
|
72
|
+
await table.deleteColumn(0);
|
|
73
|
+
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(30);
|
|
74
|
+
|
|
75
|
+
await table.deleteColumn(0);
|
|
76
|
+
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(20);
|
|
77
|
+
|
|
78
|
+
await table.deleteColumn(0);
|
|
79
|
+
await expect(table.grid.cellsWithinPlane('grid')).toHaveCount(10);
|
|
80
|
+
|
|
81
|
+
await page.getByTestId('table-column-settings-button').nth(0).click();
|
|
82
|
+
|
|
83
|
+
// Ensure that the delete menu item is not visible when there is only one column left.
|
|
84
|
+
await expect(page.getByTestId('column-delete')).toHaveCount(0);
|
|
85
|
+
await page.close();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Rest of add column test remains the same as it's a more complex flow
|
|
89
|
+
test('add column', async ({ browser }) => {
|
|
90
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
91
|
+
const table = new TableManager(page);
|
|
92
|
+
|
|
93
|
+
await table.grid.ready();
|
|
94
|
+
const newColumnLabel = 'TEST LABEL';
|
|
95
|
+
|
|
96
|
+
await table.addColumn({ label: newColumnLabel, format: 'number' });
|
|
97
|
+
|
|
98
|
+
// Delete first two columns to get the new column into view
|
|
99
|
+
await table.deleteColumn(0);
|
|
100
|
+
await table.deleteColumn(0);
|
|
101
|
+
|
|
102
|
+
await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
|
|
103
|
+
await page.close();
|
|
104
|
+
});
|
|
105
|
+
});
|
package/src/types/table.ts
CHANGED
|
@@ -6,8 +6,17 @@ import { ref, S, TypedObject } from '@dxos/echo-schema';
|
|
|
6
6
|
import { ThreadType } from '@dxos/plugin-space';
|
|
7
7
|
import { ViewType } from '@dxos/schema';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// TODO(burdon): Factor out echo dependency and define pattern for react-ui-xxx.
|
|
10
|
+
const TableSchema = S.Struct({
|
|
11
|
+
id: S.String,
|
|
10
12
|
name: S.optional(S.String),
|
|
11
13
|
view: S.optional(ref(ViewType)),
|
|
12
|
-
threads: S.optional(S.
|
|
13
|
-
})
|
|
14
|
+
threads: S.optional(S.Array(ref(ThreadType))),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
type _TableType = S.Schema.Type<typeof TableSchema>;
|
|
18
|
+
|
|
19
|
+
export class TableType extends TypedObject({
|
|
20
|
+
typename: 'dxos.org/type/Table',
|
|
21
|
+
version: '0.1.0',
|
|
22
|
+
})(TableSchema.fields) {}
|
package/src/util/grid.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
5
|
+
import { type DxGridPlaneCellIndex } from '@dxos/react-ui-grid';
|
|
6
6
|
|
|
7
7
|
// TODO(burdon): Factor out to grid.
|
|
8
8
|
export type GridCell = { col: number; row: number };
|
|
@@ -10,14 +10,14 @@ export type GridCell = { col: number; row: number };
|
|
|
10
10
|
/**
|
|
11
11
|
* Generates a string key for a cell based on its column and row indices.
|
|
12
12
|
*/
|
|
13
|
-
export const fromGridCell = ({ col, row }: GridCell):
|
|
13
|
+
export const fromGridCell = ({ col, row }: GridCell): DxGridPlaneCellIndex => {
|
|
14
14
|
return `${col},${row}` as const; // TODO(burdon): ":" delim.
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Converts a cell key string back into column and row coordinates.
|
|
19
19
|
*/
|
|
20
|
-
export const toGridCell = (cell:
|
|
20
|
+
export const toGridCell = (cell: DxGridPlaneCellIndex): GridCell => {
|
|
21
21
|
const [col, row] = cell.split(',').map(Number);
|
|
22
22
|
return { row, col };
|
|
23
23
|
};
|
|
@@ -20,29 +20,34 @@ const createButtonHtml = ({
|
|
|
20
20
|
button,
|
|
21
21
|
value = '',
|
|
22
22
|
disabled = false,
|
|
23
|
+
testId,
|
|
23
24
|
}: {
|
|
24
25
|
button: TableButton;
|
|
25
26
|
value?: string;
|
|
26
27
|
disabled?: boolean;
|
|
28
|
+
testId: string;
|
|
27
29
|
}): string => {
|
|
28
30
|
const buttonClasses = 'ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-2' as const;
|
|
29
|
-
return `<button class="${buttonClasses}" ${TABLE_ATTRS[button]}="${value}" ${disabled ? 'disabled' : ''}><svg><use href="/icons.svg#${ICONS[button]}"/></svg></button>`;
|
|
31
|
+
return `<button class="${buttonClasses}" ${TABLE_ATTRS[button]}="${value}" data-testid="${testId}" ${disabled ? 'disabled' : ''}><svg><use href="/icons.svg#${ICONS[button]}"/></svg></button>`;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
export const tableButtons = {
|
|
33
35
|
columnSettings: {
|
|
34
36
|
attr: TABLE_ATTRS.columnSettings,
|
|
35
37
|
icon: ICONS.columnSettings,
|
|
36
|
-
render: ({ fieldId }: { fieldId: string }) =>
|
|
38
|
+
render: ({ fieldId }: { fieldId: string }) =>
|
|
39
|
+
createButtonHtml({ button: 'columnSettings', value: fieldId, testId: 'table-column-settings-button' }),
|
|
37
40
|
},
|
|
38
41
|
rowMenu: {
|
|
39
42
|
attr: TABLE_ATTRS.rowMenu,
|
|
40
43
|
icon: ICONS.rowMenu,
|
|
41
|
-
render: ({ rowIndex }: { rowIndex: number }) =>
|
|
44
|
+
render: ({ rowIndex }: { rowIndex: number }) =>
|
|
45
|
+
createButtonHtml({ button: 'rowMenu', value: rowIndex.toString(), testId: 'table-row-menu-button' }),
|
|
42
46
|
},
|
|
43
47
|
addColumn: {
|
|
44
48
|
attr: TABLE_ATTRS.newColumn,
|
|
45
49
|
icon: ICONS.newColumn,
|
|
46
|
-
render: ({ disabled }: { disabled?: boolean } = {}) =>
|
|
50
|
+
render: ({ disabled }: { disabled?: boolean } = {}) =>
|
|
51
|
+
createButtonHtml({ button: 'newColumn', disabled, testId: 'table-new-column-button' }),
|
|
47
52
|
},
|
|
48
53
|
} as const;
|
|
@@ -16,7 +16,7 @@ export const renderCheckbox = ({
|
|
|
16
16
|
}: RenderCheckboxProps): string => {
|
|
17
17
|
const baseClasses = 'absolute inset-block-[6px] inline-end-[6px] ch-checkbox' as const;
|
|
18
18
|
const { attributes } = tableControls.checkbox;
|
|
19
|
-
return `<input type="checkbox" class="${baseClasses}" ${attributes.checkbox}="${rowIndex}" ${header ? attributes.header : ''} ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''}/>`;
|
|
19
|
+
return `<input type="checkbox" class="${baseClasses}" ${attributes.checkbox}="${rowIndex}" ${header ? attributes.header : ''} ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''} data-testid="table-selection"/>`;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export const tableControls = {
|
package/src/util/table.ts
CHANGED
|
@@ -20,9 +20,15 @@ import { createFieldId, createView, ViewProjection } from '@dxos/schema';
|
|
|
20
20
|
|
|
21
21
|
import { type TableType } from '../types';
|
|
22
22
|
|
|
23
|
+
type InitialiseTableProps = {
|
|
24
|
+
space: Space;
|
|
25
|
+
table: TableType;
|
|
26
|
+
initialRow?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
23
29
|
// TODO(burdon): Pass in type.
|
|
24
30
|
// TODO(burdon): User should determine typename.
|
|
25
|
-
export const initializeTable = ({ space, table
|
|
31
|
+
export const initializeTable = ({ space, table, initialRow = true }: InitialiseTableProps): MutableSchema => {
|
|
26
32
|
log.info('initializeTable', { table });
|
|
27
33
|
|
|
28
34
|
const ContactSchema = TypedObject({
|
|
@@ -70,8 +76,10 @@ export const initializeTable = ({ space, table }: { space: Space; table: TableTy
|
|
|
70
76
|
},
|
|
71
77
|
});
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
if (initialRow) {
|
|
80
|
+
// TODO(burdon): Last (first) row should not be in db and should be managed by the model.
|
|
81
|
+
space.db.add(create(contactSchema, {}));
|
|
82
|
+
}
|
|
75
83
|
|
|
76
84
|
return contactSchema;
|
|
77
85
|
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types/table.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport class TableType extends TypedObject({ typename: 'dxos.org/type/Table', version: '0.1.0' })({\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n threads: S.optional(S.mutable(S.Array(ref(ThreadType)))),\n}) {}\n"],
|
|
5
|
-
"mappings": ";AAIA,SAASA,KAAKC,GAAGC,mBAAmB;AACpC,SAASC,kBAAkB;AAC3B,SAASC,gBAAgB;AAElB,IAAMC,YAAN,cAAwBC,YAAY;EAAEC,UAAU;EAAuBC,SAAS;AAAQ,CAAA,EAAG;EAChGC,MAAMC,EAAEC,SAASD,EAAEE,MAAM;EACzBC,MAAMH,EAAEC,SAASG,IAAIC,QAAAA,CAAAA;EACrBC,SAASN,EAAEC,SAASD,EAAEO,QAAQP,EAAEQ,MAAMJ,IAAIK,UAAAA,CAAAA,CAAAA,CAAAA;AAC5C,CAAA,EAAA;AAAI;",
|
|
6
|
-
"names": ["ref", "S", "TypedObject", "ThreadType", "ViewType", "TableType", "TypedObject", "typename", "version", "name", "S", "optional", "String", "view", "ref", "ViewType", "threads", "mutable", "Array", "ThreadType"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types/table.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport class TableType extends TypedObject({ typename: 'dxos.org/type/Table', version: '0.1.0' })({\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n threads: S.optional(S.mutable(S.Array(ref(ThreadType)))),\n}) {}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAIA,yBAAoC;AACpC,0BAA2B;AAC3B,oBAAyB;AAElB,IAAMA,YAAN,kBAAwBC,gCAAY;EAAEC,UAAU;EAAuBC,SAAS;AAAQ,CAAA,EAAG;EAChGC,MAAMC,qBAAEC,SAASD,qBAAEE,MAAM;EACzBC,MAAMH,qBAAEC,aAASG,wBAAIC,sBAAAA,CAAAA;EACrBC,SAASN,qBAAEC,SAASD,qBAAEO,QAAQP,qBAAEQ,UAAMJ,wBAAIK,8BAAAA,CAAAA,CAAAA,CAAAA;AAC5C,CAAA,EAAA;AAAI;",
|
|
6
|
-
"names": ["TableType", "TypedObject", "typename", "version", "name", "S", "optional", "String", "view", "ref", "ViewType", "threads", "mutable", "Array", "ThreadType"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types/table.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport class TableType extends TypedObject({ typename: 'dxos.org/type/Table', version: '0.1.0' })({\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n threads: S.optional(S.mutable(S.Array(ref(ThreadType)))),\n}) {}\n"],
|
|
5
|
-
"mappings": ";;;AAIA,SAASA,KAAKC,GAAGC,mBAAmB;AACpC,SAASC,kBAAkB;AAC3B,SAASC,gBAAgB;AAElB,IAAMC,YAAN,cAAwBC,YAAY;EAAEC,UAAU;EAAuBC,SAAS;AAAQ,CAAA,EAAG;EAChGC,MAAMC,EAAEC,SAASD,EAAEE,MAAM;EACzBC,MAAMH,EAAEC,SAASG,IAAIC,QAAAA,CAAAA;EACrBC,SAASN,EAAEC,SAASD,EAAEO,QAAQP,EAAEQ,MAAMJ,IAAIK,UAAAA,CAAAA,CAAAA,CAAAA;AAC5C,CAAA,EAAA;AAAI;",
|
|
6
|
-
"names": ["ref", "S", "TypedObject", "ThreadType", "ViewType", "TableType", "TypedObject", "typename", "version", "name", "S", "optional", "String", "view", "ref", "ViewType", "threads", "mutable", "Array", "ThreadType"]
|
|
7
|
-
}
|