@dxos/react-ui-table 0.8.4-main.e098934 → 0.8.4-main.e8ec1fe

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 (97) hide show
  1. package/dist/lib/browser/{chunk-63UUF4IG.mjs → chunk-2PFAPQL4.mjs} +7 -7
  2. package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-BBXDTWCX.mjs → chunk-GS2UNGJK.mjs} +443 -490
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +161 -303
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +9 -7
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-K734DJB7.mjs → chunk-6POOFFYF.mjs} +7 -7
  12. package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-RNQDU7IQ.mjs → chunk-TRR3C5NX.mjs} +443 -490
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +161 -303
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +9 -7
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +1 -1
  21. package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
  22. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -3
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/Relations.stories.d.ts +70 -7
  26. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.d.ts +5 -2
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +5 -6
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -5
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -3
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -2
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  39. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  40. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  41. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  42. package/dist/types/src/components/testing.d.ts +3 -3
  43. package/dist/types/src/components/testing.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  45. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useTableModel.d.ts +3 -3
  47. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  48. package/dist/types/src/model/modal-controller.d.ts +0 -7
  49. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  50. package/dist/types/src/model/table-model.d.ts +4 -4
  51. package/dist/types/src/model/table-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  53. package/dist/types/src/model/table-sorting.d.ts +2 -2
  54. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  55. package/dist/types/src/testing/useTestTableModel.d.ts +3 -3
  56. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  57. package/dist/types/src/types/schema.d.ts +6 -6
  58. package/dist/types/src/types/schema.d.ts.map +1 -1
  59. package/dist/types/src/util/dynamic-table.d.ts +5 -6
  60. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  61. package/dist/types/src/util/schema.d.ts +1 -1
  62. package/dist/types/src/util/schema.d.ts.map +1 -1
  63. package/dist/types/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +52 -51
  65. package/src/components/Table/DynamicTable.stories.tsx +11 -10
  66. package/src/components/Table/DynamicTable.tsx +4 -7
  67. package/src/components/Table/Relations.stories.tsx +127 -18
  68. package/src/components/Table/RowActionsMenu.tsx +21 -13
  69. package/src/components/Table/Table.stories.tsx +44 -43
  70. package/src/components/Table/Table.tsx +13 -9
  71. package/src/components/TableCellEditor/FormCellEditor.tsx +51 -41
  72. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +19 -46
  73. package/src/components/TableCellEditor/TableCellEditor.tsx +7 -94
  74. package/src/components/TableCellEditor/index.ts +1 -0
  75. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  76. package/src/components/testing.ts +2 -2
  77. package/src/hooks/useAddRow.ts +1 -1
  78. package/src/hooks/useTableModel.ts +5 -5
  79. package/src/model/modal-controller.ts +0 -28
  80. package/src/model/table-model.test.ts +12 -7
  81. package/src/model/table-model.ts +12 -8
  82. package/src/model/table-presentation.test.ts +15 -18
  83. package/src/model/table-presentation.ts +4 -5
  84. package/src/model/table-sorting.ts +9 -9
  85. package/src/playwright/TableManager.ts +1 -1
  86. package/src/playwright/smoke.spec.ts +7 -5
  87. package/src/testing/useTestTableModel.ts +7 -5
  88. package/src/types/schema.ts +8 -8
  89. package/src/util/dynamic-table.ts +9 -11
  90. package/src/util/schema.ts +4 -4
  91. package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
  92. package/dist/lib/browser/chunk-BBXDTWCX.mjs.map +0 -7
  93. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
  94. package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs.map +0 -7
  95. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  96. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  97. package/src/components/Table/CreateRefPanel.tsx +0 -103
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.8.4-main.e098934",
3
+ "version": "0.8.4-main.e8ec1fe",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -53,73 +53,74 @@
53
53
  "src"
54
54
  ],
55
55
  "dependencies": {
56
- "@codemirror/autocomplete": "^6.18.7",
56
+ "@codemirror/autocomplete": "^6.19.0",
57
57
  "@codemirror/state": "^6.5.2",
58
- "@codemirror/view": "^6.38.2",
58
+ "@codemirror/view": "^6.38.4",
59
59
  "@fluentui/react-tabster": "^9.24.2",
60
60
  "@preact-signals/safe-react": "^0.9.0",
61
- "@preact/signals-core": "^1.9.0",
61
+ "@preact/signals-core": "^1.12.1",
62
62
  "@radix-ui/react-context": "1.1.1",
63
63
  "@radix-ui/react-primitive": "2.0.2",
64
64
  "@radix-ui/react-slot": "1.1.2",
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.e098934",
69
- "@dxos/context": "0.8.4-main.e098934",
70
- "@dxos/echo": "0.8.4-main.e098934",
71
- "@dxos/echo-db": "0.8.4-main.e098934",
72
- "@dxos/effect": "0.8.4-main.e098934",
73
- "@dxos/echo-schema": "0.8.4-main.e098934",
74
- "@dxos/invariant": "0.8.4-main.e098934",
75
- "@dxos/keys": "0.8.4-main.e098934",
76
- "@dxos/lit-grid": "0.8.4-main.e098934",
77
- "@dxos/live-object": "0.8.4-main.e098934",
78
- "@dxos/debug": "0.8.4-main.e098934",
79
- "@dxos/log": "0.8.4-main.e098934",
80
- "@dxos/plugin-search": "0.8.4-main.e098934",
81
- "@dxos/random": "0.8.4-main.e098934",
82
- "@dxos/react-client": "0.8.4-main.e098934",
83
- "@dxos/react-ui-attention": "0.8.4-main.e098934",
84
- "@dxos/react-ui-editor": "0.8.4-main.e098934",
85
- "@dxos/react-ui-form": "0.8.4-main.e098934",
86
- "@dxos/react-ui-grid": "0.8.4-main.e098934",
87
- "@dxos/react-ui-menu": "0.8.4-main.e098934",
88
- "@dxos/react-ui-searchlist": "0.8.4-main.e098934",
89
- "@dxos/react-ui-stack": "0.8.4-main.e098934",
90
- "@dxos/react-ui-tag-picker": "0.8.4-main.e098934",
91
- "@dxos/react-ui-types": "0.8.4-main.e098934",
92
- "@dxos/util": "0.8.4-main.e098934",
93
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.e098934",
94
- "@dxos/schema": "0.8.4-main.e098934"
68
+ "@dxos/async": "0.8.4-main.e8ec1fe",
69
+ "@dxos/debug": "0.8.4-main.e8ec1fe",
70
+ "@dxos/context": "0.8.4-main.e8ec1fe",
71
+ "@dxos/echo": "0.8.4-main.e8ec1fe",
72
+ "@dxos/echo-db": "0.8.4-main.e8ec1fe",
73
+ "@dxos/invariant": "0.8.4-main.e8ec1fe",
74
+ "@dxos/keys": "0.8.4-main.e8ec1fe",
75
+ "@dxos/lit-grid": "0.8.4-main.e8ec1fe",
76
+ "@dxos/live-object": "0.8.4-main.e8ec1fe",
77
+ "@dxos/log": "0.8.4-main.e8ec1fe",
78
+ "@dxos/plugin-search": "0.8.4-main.e8ec1fe",
79
+ "@dxos/react-client": "0.8.4-main.e8ec1fe",
80
+ "@dxos/random": "0.8.4-main.e8ec1fe",
81
+ "@dxos/effect": "0.8.4-main.e8ec1fe",
82
+ "@dxos/react-ui-attention": "0.8.4-main.e8ec1fe",
83
+ "@dxos/react-ui-form": "0.8.4-main.e8ec1fe",
84
+ "@dxos/react-ui-editor": "0.8.4-main.e8ec1fe",
85
+ "@dxos/react-ui-grid": "0.8.4-main.e8ec1fe",
86
+ "@dxos/react-ui-menu": "0.8.4-main.e8ec1fe",
87
+ "@dxos/react-ui-components": "0.8.4-main.e8ec1fe",
88
+ "@dxos/react-ui-searchlist": "0.8.4-main.e8ec1fe",
89
+ "@dxos/react-ui-stack": "0.8.4-main.e8ec1fe",
90
+ "@dxos/schema": "0.8.4-main.e8ec1fe",
91
+ "@dxos/react-ui-types": "0.8.4-main.e8ec1fe",
92
+ "@dxos/types": "0.8.4-main.e8ec1fe",
93
+ "@dxos/util": "0.8.4-main.e8ec1fe",
94
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.e8ec1fe"
95
95
  },
96
96
  "devDependencies": {
97
- "@effect-rx/rx-react": "0.38.0",
98
- "@effect/platform": "0.90.2",
97
+ "@effect-atom/atom-react": "^0.3.4",
98
+ "@effect/platform": "0.92.1",
99
99
  "@types/lodash.orderby": "^4.6.9",
100
- "@types/react": "~18.2.0",
101
- "@types/react-dom": "~18.2.0",
102
- "effect": "3.17.7",
100
+ "@types/react": "~19.2.2",
101
+ "@types/react-dom": "~19.2.2",
102
+ "effect": "3.18.3",
103
103
  "fast-check": "^3.19.0",
104
- "react": "~18.2.0",
105
- "react-dom": "~18.2.0",
106
- "vite": "7.1.1",
107
- "@dxos/plugin-search": "0.8.4-main.e098934",
108
- "@dxos/echo-signals": "0.8.4-main.e098934",
109
- "@dxos/random": "0.8.4-main.e098934",
110
- "@dxos/react-ui": "0.8.4-main.e098934",
111
- "@dxos/react-ui-theme": "0.8.4-main.e098934",
112
- "@dxos/storybook-utils": "0.8.4-main.e098934",
113
- "@dxos/test-utils": "0.8.4-main.e098934"
104
+ "react": "~19.2.0",
105
+ "react-dom": "~19.2.0",
106
+ "vite": "7.1.9",
107
+ "@dxos/lit-ui": "0.8.4-main.e8ec1fe",
108
+ "@dxos/echo-signals": "0.8.4-main.e8ec1fe",
109
+ "@dxos/random": "0.8.4-main.e8ec1fe",
110
+ "@dxos/plugin-search": "0.8.4-main.e8ec1fe",
111
+ "@dxos/react-ui": "0.8.4-main.e8ec1fe",
112
+ "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe",
113
+ "@dxos/storybook-utils": "0.8.4-main.e8ec1fe",
114
+ "@dxos/test-utils": "0.8.4-main.e8ec1fe"
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
- "react": "~18.2.0",
120
- "react-dom": "~18.2.0",
121
- "@dxos/react-ui": "0.8.4-main.e098934",
122
- "@dxos/react-ui-theme": "0.8.4-main.e098934"
120
+ "react": "^19.0.0",
121
+ "react-dom": "^19.0.0",
122
+ "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe",
123
+ "@dxos/react-ui": "0.8.4-main.e8ec1fe"
123
124
  },
124
125
  "publishConfig": {
125
126
  "access": "public"
@@ -2,20 +2,18 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useMemo, useState } from 'react';
9
7
 
10
8
  import { Obj } from '@dxos/echo';
11
- import { FormatEnum, type JsonSchemaType } from '@dxos/echo-schema';
9
+ import { FormatEnum, type JsonSchemaType } from '@dxos/echo/internal';
12
10
  import { faker } from '@dxos/random';
13
11
  import { useClient } from '@dxos/react-client';
14
12
  import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
15
13
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
14
+ import { withTheme } from '@dxos/react-ui/testing';
16
15
  import { type SchemaPropertyDefinition } from '@dxos/schema';
17
16
  import { Testing } from '@dxos/schema/testing';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
19
17
 
20
18
  import { type TableFeatures } from '../../model';
21
19
  import { translations } from '../../translations';
@@ -60,8 +58,11 @@ const DynamicTableStory = () => {
60
58
  const meta = {
61
59
  title: 'ui/react-ui-table/DynamicTable',
62
60
  component: DynamicTable,
63
- parameters: { translations },
64
- decorators: [withLayout({ fullscreen: true }), withTheme],
61
+ decorators: [withTheme],
62
+ parameters: {
63
+ layout: 'fullscreen',
64
+ translations,
65
+ },
65
66
  } satisfies Meta<typeof DynamicTable>;
66
67
 
67
68
  export default meta;
@@ -131,7 +132,7 @@ export const WithEchoSchema: StoryObj = {
131
132
  render: () => {
132
133
  const client = useClient();
133
134
  const { space } = useClientProvider();
134
- const schema = useSchema(client, space, Testing.Contact.typename);
135
+ const schema = useSchema(client, space, Testing.Person.typename);
135
136
  const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
136
137
  if (!schema) {
137
138
  return <div>Loading schema...</div>;
@@ -141,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
141
142
  },
142
143
  decorators: [
143
144
  withClientProvider({
144
- types: [Testing.Contact],
145
+ types: [Testing.Person],
145
146
  createIdentity: true,
146
147
  createSpace: true,
147
- onSpaceCreated: async ({ space }) => {
148
+ onCreateSpace: async ({ space }) => {
148
149
  Array.from({ length: 10 }).forEach(() => {
149
150
  space.db.add(
150
- Obj.make(Testing.Contact, {
151
+ Obj.make(Testing.Person, {
151
152
  name: faker.person.fullName(),
152
153
  email: faker.internet.email(),
153
154
  }),
@@ -2,10 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema } from 'effect';
5
+ import type * as Schema from 'effect/Schema';
6
6
  import React, { useCallback, useMemo, useRef } from 'react';
7
7
 
8
- import { type JsonSchemaType } from '@dxos/echo-schema';
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
11
 
@@ -45,15 +45,12 @@ export const DynamicTable = ({
45
45
  ...props
46
46
  }: DynamicTableProps) => {
47
47
  // TODO(burdon): Remove variance from the props (should be normalized externally; possibly via hooks).
48
- const { typename, jsonSchema } = useMemo(
48
+ const { jsonSchema } = useMemo(
49
49
  () => getBaseSchema({ typename: name, properties, jsonSchema: _jsonSchema, schema }),
50
50
  [name, properties, _jsonSchema, schema],
51
51
  );
52
52
 
53
- const { projection, view } = useMemo(
54
- () => makeDynamicTable({ typename, jsonSchema, properties }),
55
- [typename, jsonSchema, properties],
56
- );
53
+ const { projection, view } = useMemo(() => makeDynamicTable({ jsonSchema, properties }), [jsonSchema, properties]);
57
54
 
58
55
  const tableRef = useRef<TableController>(null);
59
56
  const handleCellUpdate = useCallback((cell: any) => {
@@ -2,20 +2,24 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React, { useEffect, useMemo, useState } from 'react';
9
-
10
- import { Type } from '@dxos/echo';
11
- import { type JsonSchemaType } from '@dxos/echo-schema';
6
+ import type * as Schema from 'effect/Schema';
7
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
8
+ import { expect, userEvent, within } from 'storybook/test';
9
+
10
+ import { Obj, Type } from '@dxos/echo';
11
+ import { type JsonSchemaType } from '@dxos/echo/internal';
12
+ import { type DxGrid } from '@dxos/lit-grid';
13
+ import '@dxos/lit-ui/dx-tag-picker.pcss';
12
14
  import { faker } from '@dxos/random';
13
15
  import { useClient } from '@dxos/react-client';
14
16
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
15
17
  import { useAsyncEffect } from '@dxos/react-ui';
16
- import { DataType } from '@dxos/schema';
18
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
19
+ import { translations as formTranslations } from '@dxos/react-ui-form';
20
+ import { View } from '@dxos/schema';
17
21
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
22
+ import { Organization, Person } from '@dxos/types';
19
23
 
20
24
  import { useTableModel } from '../../hooks';
21
25
  import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
@@ -34,7 +38,7 @@ const generator: ValueGenerator = faker as any;
34
38
  const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
35
39
  const client = useClient();
36
40
  const { space } = useClientProvider();
37
- const [view, setView] = useState<DataType.View>();
41
+ const [view, setView] = useState<View.View>();
38
42
  const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
39
43
 
40
44
  const features = useMemo<TableFeatures>(
@@ -84,27 +88,39 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
84
88
 
85
89
  const DefaultStory = () => {
86
90
  const client = useClient();
87
- const { model: orgModel, presentation: orgPresentation } = useTestModel(DataType.Organization, 50);
88
- const { model: contactModel, presentation: contactPresentation } = useTestModel(DataType.Person, 50);
91
+ const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
92
+ const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
93
+ const { space } = useClientProvider();
94
+
95
+ const handleCreate = useCallback(
96
+ (schema: Schema.Schema.AnyNoContext, values: any) => {
97
+ return client.spaces.default.db.add(Obj.make(schema, values));
98
+ },
99
+ [space],
100
+ );
89
101
 
90
102
  return (
91
103
  <div className='is-full bs-full grid grid-cols-2 divide-x divide-separator'>
92
104
  <TableComponent.Root>
93
105
  <TableComponent.Main
94
106
  model={orgModel}
95
- schema={DataType.Organization}
107
+ schema={Organization.Organization}
96
108
  presentation={orgPresentation}
109
+ onCreate={handleCreate}
97
110
  client={client}
98
111
  ignoreAttention
112
+ testId='relations-0'
99
113
  />
100
114
  </TableComponent.Root>
101
115
  <TableComponent.Root>
102
116
  <TableComponent.Main
103
117
  model={contactModel}
104
- schema={DataType.Person}
118
+ schema={Person.Person}
105
119
  presentation={contactPresentation}
120
+ onCreate={handleCreate}
106
121
  client={client}
107
122
  ignoreAttention
123
+ testId='relations-1'
108
124
  />
109
125
  </TableComponent.Root>
110
126
  </div>
@@ -114,20 +130,113 @@ const DefaultStory = () => {
114
130
  const meta = {
115
131
  title: 'ui/react-ui-table/Relations',
116
132
  render: DefaultStory,
117
- parameters: { translations, controls: { disable: true } },
118
133
  decorators: [
134
+ withTheme,
135
+ // TODO(thure): Shouldn’t `layout: 'fullscreen'` below make this unnecessary?
136
+ withLayout({ classNames: 'fixed inset-0' }),
119
137
  withClientProvider({
120
- types: [DataType.View, DataType.Organization, DataType.Person, Table.Table],
138
+ types: [View.View, Organization.Organization, Person.Person, Table.Table],
121
139
  createIdentity: true,
122
140
  createSpace: true,
123
141
  }),
124
- withTheme,
125
- withLayout({ fullscreen: true }),
126
142
  ],
143
+ parameters: {
144
+ layout: 'fullscreen',
145
+ controls: { disable: true },
146
+ translations: [...translations, ...formTranslations],
147
+ },
127
148
  } satisfies Meta<typeof DefaultStory>;
128
149
 
129
150
  export default meta;
130
151
 
131
152
  type Story = StoryObj<typeof meta>;
132
153
 
133
- export const Default: Story = {};
154
+ export const Default: Story = {
155
+ play: async ({ canvasElement }: any) => {
156
+ const canvas = within(canvasElement);
157
+ const body = within(document.body);
158
+
159
+ // Get all dx-grid elements (should have 2 - one for each table)
160
+ const firstGrid = await canvas.findByTestId('relations-0', undefined, { timeout: 30_000 });
161
+ const secondGrid = await canvas.findByTestId('relations-1', undefined, { timeout: 30_000 });
162
+
163
+ // Focus on the second table (Person/contacts table)
164
+ await expect(secondGrid).toBeVisible();
165
+
166
+ // Scroll to the relations column (column 4) - this mimics the scrollToColumn call
167
+ (secondGrid.closest('dx-grid') as DxGrid).scrollToColumn(4);
168
+
169
+ // Find and click the target cell (first row, relations column)
170
+ const targetCell = await within(secondGrid).findByTestId('grid.4.0', undefined, { timeout: 10_000 });
171
+ await expect(targetCell).toBeVisible();
172
+
173
+ // Click to focus the cell
174
+ await userEvent.click(targetCell as HTMLElement);
175
+ await expect(targetCell).toHaveFocus();
176
+ await userEvent.keyboard('{Enter}');
177
+
178
+ // Look for the combobox that should appear in edit mode
179
+ const combobox = await body.findByRole('combobox', undefined, { timeout: 5000 });
180
+ await userEvent.click(combobox);
181
+
182
+ const searchField = await body.findByPlaceholderText('Search…');
183
+ await userEvent.click(searchField);
184
+
185
+ // Get the first organization name from the first table to search for
186
+ const orgCell = await within(firstGrid).findByTestId('grid.0.0', undefined, { timeout: 10_000 });
187
+
188
+ const orgName = (orgCell.querySelector('.dx-grid__cell__content') as HTMLElement).textContent;
189
+ // Type the first 4 characters to search
190
+ await userEvent.type(searchField, orgName.substring(0, 4));
191
+
192
+ // Look for an option to select
193
+ const option = await body.findAllByRole('option');
194
+ await expect(option[0]).toBeVisible();
195
+
196
+ // Press Enter to select
197
+ await userEvent.keyboard('{Enter}');
198
+
199
+ // Look for and click save button
200
+ const saveButton = await body.findByTestId('save-button');
201
+ await userEvent.click(saveButton);
202
+
203
+ // Verify the relation was set (cell should now contain the org name)
204
+ await expect(targetCell).toHaveTextContent(orgName.substring(0, 4));
205
+
206
+ // Test object creation (new relations) - equivalent to "new relations work as expected" test
207
+ // Find a different cell to test object creation (second row, relations column)
208
+ const newTargetCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 10_000 });
209
+ await expect(newTargetCell).toBeTruthy();
210
+
211
+ // Click to focus the cell
212
+ await userEvent.click(newTargetCell as Element);
213
+
214
+ await userEvent.keyboard('{Enter}');
215
+
216
+ // Look for the combobox that should appear in edit mode
217
+ const newCombobox = await body.findByRole('combobox');
218
+ await userEvent.click(newCombobox);
219
+
220
+ const newSearchField = await body.findByPlaceholderText('Search…');
221
+ await userEvent.click(newSearchField);
222
+
223
+ // Type a new object name (this will create a new object)
224
+ const newOrgName = 'Salieri LLC';
225
+ await userEvent.type(newSearchField, newOrgName);
226
+
227
+ // Look for an option to select (should be the create new option)
228
+ const newOption = await body.findAllByRole('option');
229
+ await expect(newOption[0]).toBeVisible();
230
+
231
+ // Press Enter to select/create
232
+ await userEvent.keyboard('{Enter}');
233
+
234
+ // Look for and click save button
235
+ const createReferencedObjectForm = await body.findByTestId('create-referenced-object-form');
236
+ const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
237
+ await userEvent.click(saveObjectButton);
238
+
239
+ // Verify the new object was created and relation was set
240
+ await expect(newTargetCell).toHaveTextContent(newOrgName);
241
+ },
242
+ } as any;
@@ -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>