@dxos/react-ui-table 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d

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.
Files changed (92) hide show
  1. package/dist/lib/browser/chunk-5XWSB3JP.mjs +71 -0
  2. package/dist/lib/browser/chunk-5XWSB3JP.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-OZTXLZGM.mjs → chunk-KNC7XYT6.mjs} +521 -542
  4. package/dist/lib/browser/chunk-KNC7XYT6.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +75 -59
  6. package/dist/lib/browser/index.mjs.map +3 -3
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +14 -27
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +3 -3
  11. package/dist/lib/node-esm/{chunk-S56Z4V4X.mjs → chunk-CWSGS3Y2.mjs} +521 -542
  12. package/dist/lib/node-esm/chunk-CWSGS3Y2.mjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-MKTMOY46.mjs +72 -0
  14. package/dist/lib/node-esm/chunk-MKTMOY46.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +75 -59
  16. package/dist/lib/node-esm/index.mjs.map +3 -3
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +14 -27
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +3 -3
  21. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +2 -2
  23. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/Table/Relations.stories.d.ts +7 -7
  25. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/Table.stories.d.ts +67 -4
  28. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  30. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +2 -2
  31. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  33. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  34. package/dist/types/src/components/testing.d.ts +0 -2
  35. package/dist/types/src/components/testing.d.ts.map +1 -1
  36. package/dist/types/src/hooks/index.d.ts +2 -1
  37. package/dist/types/src/hooks/index.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useProjectionModel.d.ts +5 -0
  39. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  40. package/dist/types/src/hooks/useTableModel.d.ts +4 -5
  41. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  42. package/dist/types/src/model/table-model.d.ts +7 -9
  43. package/dist/types/src/model/table-model.d.ts.map +1 -1
  44. package/dist/types/src/model/table-sorting.d.ts +8 -4
  45. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  46. package/dist/types/src/playwright/TableManager.d.ts +1 -1
  47. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  48. package/dist/types/src/testing/useTestTableModel.d.ts +21 -15
  49. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  50. package/dist/types/src/translations.d.ts +2 -2
  51. package/dist/types/src/types/Table.d.ts +28 -0
  52. package/dist/types/src/types/Table.d.ts.map +1 -0
  53. package/dist/types/src/types/index.d.ts +1 -1
  54. package/dist/types/src/types/index.d.ts.map +1 -1
  55. package/dist/types/src/util/dynamic-table.d.ts +7 -6
  56. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  57. package/dist/types/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +41 -40
  59. package/src/components/Table/DynamicTable.stories.tsx +7 -7
  60. package/src/components/Table/DynamicTable.tsx +12 -4
  61. package/src/components/Table/Relations.stories.tsx +24 -35
  62. package/src/components/Table/RowActionsMenu.tsx +21 -13
  63. package/src/components/Table/Table.stories.tsx +58 -53
  64. package/src/components/TableCellEditor/FormCellEditor.tsx +11 -11
  65. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +11 -9
  66. package/src/components/TableCellEditor/TableCellEditor.tsx +4 -4
  67. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  68. package/src/components/testing.ts +3 -3
  69. package/src/hooks/index.ts +2 -1
  70. package/src/hooks/useProjectionModel.ts +19 -0
  71. package/src/hooks/useTableModel.ts +11 -13
  72. package/src/model/table-model.test.ts +5 -5
  73. package/src/model/table-model.ts +34 -40
  74. package/src/model/table-presentation.test.ts +5 -5
  75. package/src/model/table-presentation.ts +9 -9
  76. package/src/model/table-sorting.ts +28 -19
  77. package/src/playwright/TableManager.ts +3 -3
  78. package/src/playwright/smoke.spec.ts +8 -6
  79. package/src/testing/useTestTableModel.ts +32 -24
  80. package/src/translations.ts +2 -2
  81. package/src/types/Table.ts +89 -0
  82. package/src/types/index.ts +1 -1
  83. package/src/util/dynamic-table.ts +12 -16
  84. package/dist/lib/browser/chunk-4K7JPALT.mjs +0 -66
  85. package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
  86. package/dist/lib/browser/chunk-OZTXLZGM.mjs.map +0 -7
  87. package/dist/lib/node-esm/chunk-MK6OVUF6.mjs +0 -67
  88. package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +0 -7
  89. package/dist/lib/node-esm/chunk-S56Z4V4X.mjs.map +0 -7
  90. package/dist/types/src/types/schema.d.ts +0 -28
  91. package/dist/types/src/types/schema.d.ts.map +0 -1
  92. package/src/types/schema.ts +0 -73
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.8.4-main.5ad4a44",
3
+ "version": "0.8.4-main.66e292d",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -65,61 +65,62 @@
65
65
  "@radix-ui/react-use-controllable-state": "1.1.0",
66
66
  "date-fns": "^3.3.1",
67
67
  "lodash.orderby": "^4.6.0",
68
- "@dxos/async": "0.8.4-main.5ad4a44",
69
- "@dxos/debug": "0.8.4-main.5ad4a44",
70
- "@dxos/effect": "0.8.4-main.5ad4a44",
71
- "@dxos/context": "0.8.4-main.5ad4a44",
72
- "@dxos/echo-db": "0.8.4-main.5ad4a44",
73
- "@dxos/invariant": "0.8.4-main.5ad4a44",
74
- "@dxos/keys": "0.8.4-main.5ad4a44",
75
- "@dxos/lit-grid": "0.8.4-main.5ad4a44",
76
- "@dxos/plugin-search": "0.8.4-main.5ad4a44",
77
- "@dxos/log": "0.8.4-main.5ad4a44",
78
- "@dxos/echo": "0.8.4-main.5ad4a44",
79
- "@dxos/live-object": "0.8.4-main.5ad4a44",
80
- "@dxos/react-client": "0.8.4-main.5ad4a44",
81
- "@dxos/random": "0.8.4-main.5ad4a44",
82
- "@dxos/react-ui-attention": "0.8.4-main.5ad4a44",
83
- "@dxos/react-ui-components": "0.8.4-main.5ad4a44",
84
- "@dxos/react-ui-editor": "0.8.4-main.5ad4a44",
85
- "@dxos/react-ui-form": "0.8.4-main.5ad4a44",
86
- "@dxos/react-ui-menu": "0.8.4-main.5ad4a44",
87
- "@dxos/react-ui-grid": "0.8.4-main.5ad4a44",
88
- "@dxos/react-ui-searchlist": "0.8.4-main.5ad4a44",
89
- "@dxos/react-ui-stack": "0.8.4-main.5ad4a44",
90
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.5ad4a44",
91
- "@dxos/react-ui-types": "0.8.4-main.5ad4a44",
92
- "@dxos/schema": "0.8.4-main.5ad4a44",
93
- "@dxos/util": "0.8.4-main.5ad4a44"
68
+ "@dxos/async": "0.8.4-main.66e292d",
69
+ "@dxos/context": "0.8.4-main.66e292d",
70
+ "@dxos/debug": "0.8.4-main.66e292d",
71
+ "@dxos/echo-db": "0.8.4-main.66e292d",
72
+ "@dxos/effect": "0.8.4-main.66e292d",
73
+ "@dxos/invariant": "0.8.4-main.66e292d",
74
+ "@dxos/keys": "0.8.4-main.66e292d",
75
+ "@dxos/echo": "0.8.4-main.66e292d",
76
+ "@dxos/lit-grid": "0.8.4-main.66e292d",
77
+ "@dxos/live-object": "0.8.4-main.66e292d",
78
+ "@dxos/log": "0.8.4-main.66e292d",
79
+ "@dxos/plugin-search": "0.8.4-main.66e292d",
80
+ "@dxos/random": "0.8.4-main.66e292d",
81
+ "@dxos/react-ui-attention": "0.8.4-main.66e292d",
82
+ "@dxos/react-ui-components": "0.8.4-main.66e292d",
83
+ "@dxos/react-ui-editor": "0.8.4-main.66e292d",
84
+ "@dxos/react-client": "0.8.4-main.66e292d",
85
+ "@dxos/react-ui-grid": "0.8.4-main.66e292d",
86
+ "@dxos/react-ui-menu": "0.8.4-main.66e292d",
87
+ "@dxos/react-ui-form": "0.8.4-main.66e292d",
88
+ "@dxos/react-ui-stack": "0.8.4-main.66e292d",
89
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.66e292d",
90
+ "@dxos/react-ui-types": "0.8.4-main.66e292d",
91
+ "@dxos/schema": "0.8.4-main.66e292d",
92
+ "@dxos/util": "0.8.4-main.66e292d",
93
+ "@dxos/react-ui-searchlist": "0.8.4-main.66e292d",
94
+ "@dxos/types": "0.8.4-main.66e292d"
94
95
  },
95
96
  "devDependencies": {
96
- "@effect-rx/rx-react": "0.42.4",
97
+ "@effect-atom/atom-react": "^0.3.4",
97
98
  "@effect/platform": "0.92.1",
98
99
  "@types/lodash.orderby": "^4.6.9",
99
100
  "@types/react": "~19.2.2",
100
- "@types/react-dom": "~19.2.1",
101
+ "@types/react-dom": "~19.2.2",
101
102
  "effect": "3.18.3",
102
103
  "fast-check": "^3.19.0",
103
104
  "react": "~19.2.0",
104
105
  "react-dom": "~19.2.0",
105
106
  "vite": "7.1.9",
106
- "@dxos/echo-signals": "0.8.4-main.5ad4a44",
107
- "@dxos/random": "0.8.4-main.5ad4a44",
108
- "@dxos/lit-ui": "0.8.4-main.5ad4a44",
109
- "@dxos/plugin-search": "0.8.4-main.5ad4a44",
110
- "@dxos/react-ui": "0.8.4-main.5ad4a44",
111
- "@dxos/react-ui-theme": "0.8.4-main.5ad4a44",
112
- "@dxos/test-utils": "0.8.4-main.5ad4a44",
113
- "@dxos/storybook-utils": "0.8.4-main.5ad4a44"
107
+ "@dxos/echo-signals": "0.8.4-main.66e292d",
108
+ "@dxos/plugin-search": "0.8.4-main.66e292d",
109
+ "@dxos/lit-ui": "0.8.4-main.66e292d",
110
+ "@dxos/random": "0.8.4-main.66e292d",
111
+ "@dxos/react-ui": "0.8.4-main.66e292d",
112
+ "@dxos/react-ui-theme": "0.8.4-main.66e292d",
113
+ "@dxos/storybook-utils": "0.8.4-main.66e292d",
114
+ "@dxos/test-utils": "0.8.4-main.66e292d"
114
115
  },
115
116
  "peerDependencies": {
116
- "@effect-rx/rx-react": "^0.34.1",
117
+ "@effect-atom/atom-react": "^0.3.4",
117
118
  "@effect/platform": "^0.80.12",
118
119
  "effect": "^3.13.3",
119
120
  "react": "^19.0.0",
120
121
  "react-dom": "^19.0.0",
121
- "@dxos/react-ui": "0.8.4-main.5ad4a44",
122
- "@dxos/react-ui-theme": "0.8.4-main.5ad4a44"
122
+ "@dxos/react-ui": "0.8.4-main.66e292d",
123
+ "@dxos/react-ui-theme": "0.8.4-main.66e292d"
123
124
  },
124
125
  "publishConfig": {
125
126
  "access": "public"
@@ -6,14 +6,14 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useMemo, useState } from 'react';
7
7
 
8
8
  import { Obj } from '@dxos/echo';
9
- import { FormatEnum, type JsonSchemaType } from '@dxos/echo/internal';
9
+ import { Format, type JsonSchemaType } from '@dxos/echo/internal';
10
10
  import { faker } from '@dxos/random';
11
11
  import { useClient } from '@dxos/react-client';
12
12
  import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
13
13
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
14
14
  import { withTheme } from '@dxos/react-ui/testing';
15
15
  import { type SchemaPropertyDefinition } from '@dxos/schema';
16
- import { Testing } from '@dxos/schema/testing';
16
+ import { TestSchema } from '@dxos/schema/testing';
17
17
 
18
18
  import { type TableFeatures } from '../../model';
19
19
  import { translations } from '../../translations';
@@ -25,8 +25,8 @@ faker.seed(0);
25
25
  const useTestPropertiesAndObjects = () => {
26
26
  const properties = useMemo<SchemaPropertyDefinition[]>(
27
27
  () => [
28
- { name: 'name', format: FormatEnum.String },
29
- { name: 'age', format: FormatEnum.Number },
28
+ { name: 'name', format: Format.TypeFormat.String },
29
+ { name: 'age', format: Format.TypeFormat.Number },
30
30
  ],
31
31
  [],
32
32
  );
@@ -132,7 +132,7 @@ export const WithEchoSchema: StoryObj = {
132
132
  render: () => {
133
133
  const client = useClient();
134
134
  const { space } = useClientProvider();
135
- const schema = useSchema(client, space, Testing.Contact.typename);
135
+ const schema = useSchema(client, space, TestSchema.Person.typename);
136
136
  const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
137
137
  if (!schema) {
138
138
  return <div>Loading schema...</div>;
@@ -142,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
142
142
  },
143
143
  decorators: [
144
144
  withClientProvider({
145
- types: [Testing.Contact],
145
+ types: [TestSchema.Person],
146
146
  createIdentity: true,
147
147
  createSpace: true,
148
148
  onCreateSpace: async ({ space }) => {
149
149
  Array.from({ length: 10 }).forEach(() => {
150
150
  space.db.add(
151
- Obj.make(Testing.Contact, {
151
+ Obj.make(TestSchema.Person, {
152
152
  name: faker.person.fullName(),
153
153
  email: faker.internet.email(),
154
154
  }),
@@ -8,6 +8,7 @@ import React, { useCallback, useMemo, useRef } from 'react';
8
8
  import { type JsonSchemaType } from '@dxos/echo/internal';
9
9
  import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
10
10
  import { mx } from '@dxos/react-ui-theme';
11
+ import { ProjectionModel } from '@dxos/schema';
11
12
 
12
13
  import { useTableModel } from '../../hooks';
13
14
  import { type TableFeatures, TablePresentation, type TableRowAction } from '../../model';
@@ -50,7 +51,7 @@ export const DynamicTable = ({
50
51
  [name, properties, _jsonSchema, schema],
51
52
  );
52
53
 
53
- const { projection, view } = useMemo(() => makeDynamicTable({ jsonSchema, properties }), [jsonSchema, properties]);
54
+ const { object, projection } = useMemo(() => makeDynamicTable({ jsonSchema, properties }), [jsonSchema, properties]);
54
55
 
55
56
  const tableRef = useRef<TableController>(null);
56
57
  const handleCellUpdate = useCallback((cell: any) => {
@@ -70,11 +71,18 @@ export const DynamicTable = ({
70
71
  }) as const,
71
72
  );
72
73
 
74
+ const projectionModel = useMemo(() => {
75
+ if (jsonSchema && projection) {
76
+ const model = new ProjectionModel(jsonSchema, projection);
77
+ model.normalizeView();
78
+ return model;
79
+ }
80
+ }, [jsonSchema, projection]);
81
+
73
82
  const model = useTableModel({
74
83
  rows,
75
- view,
76
- schema: jsonSchema,
77
- projection,
84
+ object,
85
+ projection: projectionModel,
78
86
  features,
79
87
  rowActions,
80
88
  onCellUpdate: handleCellUpdate,
@@ -8,19 +8,19 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
8
8
  import { expect, userEvent, within } from 'storybook/test';
9
9
 
10
10
  import { Obj, Type } from '@dxos/echo';
11
- import { type JsonSchemaType } from '@dxos/echo/internal';
12
11
  import { type DxGrid } from '@dxos/lit-grid';
13
12
  import '@dxos/lit-ui/dx-tag-picker.pcss';
14
13
  import { faker } from '@dxos/random';
15
14
  import { useClient } from '@dxos/react-client';
16
15
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
17
16
  import { useAsyncEffect } from '@dxos/react-ui';
18
- import { withTheme } from '@dxos/react-ui/testing';
17
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
19
18
  import { translations as formTranslations } from '@dxos/react-ui-form';
20
- import { DataType } from '@dxos/schema';
19
+ import { View } from '@dxos/schema';
21
20
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
21
+ import { Organization, Person } from '@dxos/types';
22
22
 
23
- import { useTableModel } from '../../hooks';
23
+ import { useProjectionModel, useTableModel } from '../../hooks';
24
24
  import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
25
25
  import { translations } from '../../translations';
26
26
  import { Table } from '../../types';
@@ -37,8 +37,7 @@ const generator: ValueGenerator = faker as any;
37
37
  const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
38
38
  const client = useClient();
39
39
  const { space } = useClientProvider();
40
- const [view, setView] = useState<DataType.View>();
41
- const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
40
+ const [object, setObject] = useState<Table.Table>();
42
41
 
43
42
  const features = useMemo<TableFeatures>(
44
43
  () => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
@@ -50,18 +49,15 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
50
49
  return;
51
50
  }
52
51
 
53
- const {
54
- jsonSchema,
55
- schema: effectSchema,
56
- view,
57
- } = await Table.makeView({ client, space, typename: Type.getTypename(schema) });
58
- setJsonSchema(jsonSchema);
59
- setView(view);
60
- space.db.add(view);
61
- await space.db.schemaRegistry.register([effectSchema]);
52
+ const { view, jsonSchema } = await View.makeFromSpace({ client, space, typename: Type.getTypename(schema) });
53
+ const object = Table.make({ view, jsonSchema });
54
+ setObject(object);
55
+ space.db.add(object);
56
+ await space.db.schemaRegistry.register([schema]);
62
57
  }, [client, space, schema]);
63
58
 
64
- const model = useTableModel<TableRow>({ view, schema: jsonSchema, rows: [], features });
59
+ const projection = useProjectionModel(schema, object);
60
+ const model = useTableModel<TableRow>({ object, projection, features });
65
61
 
66
62
  useEffect(() => {
67
63
  if (!model || !space) {
@@ -87,8 +83,8 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
87
83
 
88
84
  const DefaultStory = () => {
89
85
  const client = useClient();
90
- const { model: orgModel, presentation: orgPresentation } = useTestModel(DataType.Organization, 50);
91
- const { model: contactModel, presentation: contactPresentation } = useTestModel(DataType.Person, 50);
86
+ const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
87
+ const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
92
88
  const { space } = useClientProvider();
93
89
 
94
90
  const handleCreate = useCallback(
@@ -103,7 +99,7 @@ const DefaultStory = () => {
103
99
  <TableComponent.Root>
104
100
  <TableComponent.Main
105
101
  model={orgModel}
106
- schema={DataType.Organization}
102
+ schema={Organization.Organization}
107
103
  presentation={orgPresentation}
108
104
  onCreate={handleCreate}
109
105
  client={client}
@@ -114,7 +110,7 @@ const DefaultStory = () => {
114
110
  <TableComponent.Root>
115
111
  <TableComponent.Main
116
112
  model={contactModel}
117
- schema={DataType.Person}
113
+ schema={Person.Person}
118
114
  presentation={contactPresentation}
119
115
  onCreate={handleCreate}
120
116
  client={client}
@@ -131,8 +127,10 @@ const meta = {
131
127
  render: DefaultStory,
132
128
  decorators: [
133
129
  withTheme,
130
+ // TODO(thure): Shouldn’t `layout: 'fullscreen'` below make this unnecessary?
131
+ withLayout({ classNames: 'fixed inset-0' }),
134
132
  withClientProvider({
135
- types: [DataType.View, DataType.Organization, DataType.Person, Table.Table],
133
+ types: [View.View, Organization.Organization, Person.Person, Table.Table],
136
134
  createIdentity: true,
137
135
  createSpace: true,
138
136
  }),
@@ -149,9 +147,7 @@ export default meta;
149
147
  type Story = StoryObj<typeof meta>;
150
148
 
151
149
  export const Default: Story = {
152
- // TODO(wittjosiah): This test is flaky, especially in CI.
153
- // Most typical failure is `targetCell` returns `null`.
154
- _play: async ({ canvasElement }: any) => {
150
+ play: async ({ canvasElement }: any) => {
155
151
  const canvas = within(canvasElement);
156
152
  const body = within(document.body);
157
153
 
@@ -165,11 +161,8 @@ export const Default: Story = {
165
161
  // Scroll to the relations column (column 4) - this mimics the scrollToColumn call
166
162
  (secondGrid.closest('dx-grid') as DxGrid).scrollToColumn(4);
167
163
 
168
- // Wait for scroll to complete
169
- await new Promise((resolve) => setTimeout(resolve, 1000));
170
-
171
164
  // Find and click the target cell (first row, relations column)
172
- const targetCell = secondGrid.querySelector('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="4"]');
165
+ const targetCell = await within(secondGrid).findByTestId('grid.4.0', undefined, { timeout: 10_000 });
173
166
  await expect(targetCell).toBeVisible();
174
167
 
175
168
  // Click to focus the cell
@@ -185,11 +178,9 @@ export const Default: Story = {
185
178
  await userEvent.click(searchField);
186
179
 
187
180
  // Get the first organization name from the first table to search for
188
- const orgCell = firstGrid.querySelector(
189
- '[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="0"] .dx-grid__cell__content',
190
- ) as HTMLElement;
181
+ const orgCell = await within(firstGrid).findByTestId('grid.0.0', undefined, { timeout: 10_000 });
191
182
 
192
- const orgName = orgCell.textContent;
183
+ const orgName = (orgCell.querySelector('.dx-grid__cell__content') as HTMLElement).textContent;
193
184
  // Type the first 4 characters to search
194
185
  await userEvent.type(searchField, orgName.substring(0, 4));
195
186
 
@@ -209,9 +200,7 @@ export const Default: Story = {
209
200
 
210
201
  // Test object creation (new relations) - equivalent to "new relations work as expected" test
211
202
  // Find a different cell to test object creation (second row, relations column)
212
- const newTargetCell = secondGrid.querySelector(
213
- '[data-dx-grid-plane="grid"] [aria-rowindex="1"][aria-colindex="4"]',
214
- );
203
+ const newTargetCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 10_000 });
215
204
  await expect(newTargetCell).toBeTruthy();
216
205
 
217
206
  // Click to focus the cell
@@ -23,24 +23,32 @@ export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
23
23
  <DropdownMenu.VirtualTrigger virtualRef={modals.trigger} />
24
24
  <DropdownMenu.Content>
25
25
  <DropdownMenu.Viewport>
26
+ {/* Custom actions */}
27
+ {model.rowActions?.length > 0 && (
28
+ <>
29
+ <DropdownMenu.Group>
30
+ {model.rowActions?.map((action) => (
31
+ <DropdownMenu.Item
32
+ key={action.id}
33
+ data-testid={`row-action-${action.id}`}
34
+ onClick={() => {
35
+ modals.close();
36
+ model.handleRowAction(action.id, state.rowIndex);
37
+ }}
38
+ >
39
+ {toLocalizedString(action.label, t)}
40
+ </DropdownMenu.Item>
41
+ ))}
42
+ </DropdownMenu.Group>
43
+ <DropdownMenu.Separator />
44
+ </>
45
+ )}
46
+ {/* Default actions */}
26
47
  {model.features.dataEditable !== false && (
27
48
  <DropdownMenu.Item data-testid='row-menu-delete' onClick={() => model.deleteRow(state.rowIndex)}>
28
49
  {t(hasSelection ? 'bulk delete row label' : 'delete row label')}
29
50
  </DropdownMenu.Item>
30
51
  )}
31
- {/* Custom actions */}
32
- {model.rowActions?.map((action) => (
33
- <DropdownMenu.Item
34
- key={action.id}
35
- data-testid={`row-action-${action.id}`}
36
- onClick={() => {
37
- modals.close();
38
- model.handleRowAction(action.id, state.rowIndex);
39
- }}
40
- >
41
- {toLocalizedString(action.label, t)}
42
- </DropdownMenu.Item>
43
- ))}
44
52
  </DropdownMenu.Viewport>
45
53
  <DropdownMenu.Arrow />
46
54
  </DropdownMenu.Content>