@dxos/react-ui-table 0.8.4-main.28f8d3d → 0.8.4-main.2c6827d

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 (114) hide show
  1. package/dist/lib/browser/chunk-2PFAPQL4.mjs +66 -0
  2. package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-ZAOSIG3L.mjs → chunk-GS2UNGJK.mjs} +174 -213
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +411 -515
  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 +12 -9
  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-6POOFFYF.mjs +67 -0
  12. package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-ZOFFI3JJ.mjs → chunk-TRR3C5NX.mjs} +174 -213
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +411 -515
  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 +12 -9
  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 +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 +51 -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 +103 -5
  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 +8 -5
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +41 -3
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -6
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -13
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -4
  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 +3 -2
  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 +9 -8
  51. package/dist/types/src/model/table-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-presentation.d.ts +1 -1
  53. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  54. package/dist/types/src/model/table-sorting.d.ts +2 -3
  55. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  56. package/dist/types/src/testing/useTestTableModel.d.ts +3 -22
  57. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  58. package/dist/types/src/types/index.d.ts +1 -1
  59. package/dist/types/src/types/index.d.ts.map +1 -1
  60. package/dist/types/src/types/schema.d.ts +25 -5
  61. package/dist/types/src/types/schema.d.ts.map +1 -1
  62. package/dist/types/src/util/dynamic-table.d.ts +5 -6
  63. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  64. package/dist/types/src/util/index.d.ts +0 -1
  65. package/dist/types/src/util/index.d.ts.map +1 -1
  66. package/dist/types/src/util/schema.d.ts +1 -1
  67. package/dist/types/src/util/schema.d.ts.map +1 -1
  68. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  69. package/dist/types/tsconfig.tsbuildinfo +1 -1
  70. package/package.json +53 -51
  71. package/src/components/Table/DynamicTable.stories.tsx +15 -12
  72. package/src/components/Table/DynamicTable.tsx +4 -7
  73. package/src/components/Table/Relations.stories.tsx +160 -42
  74. package/src/components/Table/RowActionsMenu.tsx +21 -13
  75. package/src/components/Table/Table.stories.tsx +63 -56
  76. package/src/components/Table/Table.tsx +109 -90
  77. package/src/components/TableCellEditor/FormCellEditor.tsx +153 -42
  78. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -64
  79. package/src/components/TableCellEditor/TableCellEditor.tsx +62 -208
  80. package/src/components/TableCellEditor/index.ts +1 -0
  81. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  82. package/src/components/testing.ts +2 -2
  83. package/src/hooks/useAddRow.ts +7 -5
  84. package/src/hooks/useTableModel.ts +5 -5
  85. package/src/model/modal-controller.ts +0 -28
  86. package/src/model/table-model.test.ts +19 -11
  87. package/src/model/table-model.ts +36 -24
  88. package/src/model/table-presentation.test.ts +22 -21
  89. package/src/model/table-presentation.ts +58 -39
  90. package/src/model/table-sorting.ts +16 -7
  91. package/src/playwright/TableManager.ts +1 -1
  92. package/src/playwright/smoke.spec.ts +51 -49
  93. package/src/testing/useTestTableModel.ts +10 -7
  94. package/src/types/index.ts +1 -1
  95. package/src/types/schema.ts +63 -6
  96. package/src/util/dynamic-table.ts +15 -16
  97. package/src/util/index.ts +0 -1
  98. package/src/util/schema.ts +4 -4
  99. package/src/util/table-buttons.ts +3 -3
  100. package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
  101. package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
  102. package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +0 -7
  103. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
  104. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
  105. package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +0 -7
  106. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  107. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  108. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  109. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  110. package/dist/types/src/util/table.d.ts +0 -12
  111. package/dist/types/src/util/table.d.ts.map +0 -1
  112. package/src/components/Table/CreateRefPanel.tsx +0 -86
  113. package/src/components/TableCellEditor/extension.ts +0 -61
  114. package/src/util/table.ts +0 -53
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.8.4-main.28f8d3d",
3
+ "version": "0.8.4-main.2c6827d",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -53,72 +53,74 @@
53
53
  "src"
54
54
  ],
55
55
  "dependencies": {
56
- "@codemirror/autocomplete": "^6.18.1",
57
- "@codemirror/state": "^6.4.1",
58
- "@codemirror/view": "^6.34.1",
56
+ "@codemirror/autocomplete": "^6.19.0",
57
+ "@codemirror/state": "^6.5.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/context": "0.8.4-main.28f8d3d",
69
- "@dxos/debug": "0.8.4-main.28f8d3d",
70
- "@dxos/echo": "0.8.4-main.28f8d3d",
71
- "@dxos/echo-db": "0.8.4-main.28f8d3d",
72
- "@dxos/echo-schema": "0.8.4-main.28f8d3d",
73
- "@dxos/effect": "0.8.4-main.28f8d3d",
74
- "@dxos/keys": "0.8.4-main.28f8d3d",
75
- "@dxos/lit-grid": "0.8.4-main.28f8d3d",
76
- "@dxos/live-object": "0.8.4-main.28f8d3d",
77
- "@dxos/log": "0.8.4-main.28f8d3d",
78
- "@dxos/invariant": "0.8.4-main.28f8d3d",
79
- "@dxos/plugin-search": "0.8.4-main.28f8d3d",
80
- "@dxos/react-client": "0.8.4-main.28f8d3d",
81
- "@dxos/react-ui-attention": "0.8.4-main.28f8d3d",
82
- "@dxos/random": "0.8.4-main.28f8d3d",
83
- "@dxos/react-ui-form": "0.8.4-main.28f8d3d",
84
- "@dxos/react-ui-editor": "0.8.4-main.28f8d3d",
85
- "@dxos/react-ui-grid": "0.8.4-main.28f8d3d",
86
- "@dxos/react-ui-menu": "0.8.4-main.28f8d3d",
87
- "@dxos/react-ui-searchlist": "0.8.4-main.28f8d3d",
88
- "@dxos/react-ui-stack": "0.8.4-main.28f8d3d",
89
- "@dxos/react-ui-tag-picker": "0.8.4-main.28f8d3d",
90
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.28f8d3d",
91
- "@dxos/schema": "0.8.4-main.28f8d3d",
92
- "@dxos/util": "0.8.4-main.28f8d3d",
93
- "@dxos/react-ui-types": "0.8.4-main.28f8d3d"
68
+ "@dxos/async": "0.8.4-main.2c6827d",
69
+ "@dxos/debug": "0.8.4-main.2c6827d",
70
+ "@dxos/context": "0.8.4-main.2c6827d",
71
+ "@dxos/effect": "0.8.4-main.2c6827d",
72
+ "@dxos/invariant": "0.8.4-main.2c6827d",
73
+ "@dxos/keys": "0.8.4-main.2c6827d",
74
+ "@dxos/echo": "0.8.4-main.2c6827d",
75
+ "@dxos/live-object": "0.8.4-main.2c6827d",
76
+ "@dxos/plugin-search": "0.8.4-main.2c6827d",
77
+ "@dxos/log": "0.8.4-main.2c6827d",
78
+ "@dxos/echo-db": "0.8.4-main.2c6827d",
79
+ "@dxos/random": "0.8.4-main.2c6827d",
80
+ "@dxos/react-client": "0.8.4-main.2c6827d",
81
+ "@dxos/react-ui-attention": "0.8.4-main.2c6827d",
82
+ "@dxos/react-ui-components": "0.8.4-main.2c6827d",
83
+ "@dxos/lit-grid": "0.8.4-main.2c6827d",
84
+ "@dxos/react-ui-form": "0.8.4-main.2c6827d",
85
+ "@dxos/react-ui-editor": "0.8.4-main.2c6827d",
86
+ "@dxos/react-ui-grid": "0.8.4-main.2c6827d",
87
+ "@dxos/react-ui-searchlist": "0.8.4-main.2c6827d",
88
+ "@dxos/react-ui-menu": "0.8.4-main.2c6827d",
89
+ "@dxos/react-ui-types": "0.8.4-main.2c6827d",
90
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.2c6827d",
91
+ "@dxos/types": "0.8.4-main.2c6827d",
92
+ "@dxos/schema": "0.8.4-main.2c6827d",
93
+ "@dxos/react-ui-stack": "0.8.4-main.2c6827d",
94
+ "@dxos/util": "0.8.4-main.2c6827d"
94
95
  },
95
96
  "devDependencies": {
96
- "@effect-rx/rx-react": "0.38.0",
97
- "@effect/platform": "0.90.2",
97
+ "@effect-atom/atom-react": "^0.3.4",
98
+ "@effect/platform": "0.92.1",
98
99
  "@types/lodash.orderby": "^4.6.9",
99
- "@types/react": "~18.2.0",
100
- "@types/react-dom": "~18.2.0",
101
- "effect": "3.17.7",
100
+ "@types/react": "~19.2.2",
101
+ "@types/react-dom": "~19.2.2",
102
+ "effect": "3.18.3",
102
103
  "fast-check": "^3.19.0",
103
- "react": "~18.2.0",
104
- "react-dom": "~18.2.0",
105
- "vite": "5.4.7",
106
- "@dxos/echo-signals": "0.8.4-main.28f8d3d",
107
- "@dxos/plugin-search": "0.8.4-main.28f8d3d",
108
- "@dxos/react-ui-theme": "0.8.4-main.28f8d3d",
109
- "@dxos/storybook-utils": "0.8.4-main.28f8d3d",
110
- "@dxos/react-ui": "0.8.4-main.28f8d3d",
111
- "@dxos/test-utils": "0.8.4-main.28f8d3d",
112
- "@dxos/random": "0.8.4-main.28f8d3d"
104
+ "react": "~19.2.0",
105
+ "react-dom": "~19.2.0",
106
+ "vite": "7.1.9",
107
+ "@dxos/echo-signals": "0.8.4-main.2c6827d",
108
+ "@dxos/lit-ui": "0.8.4-main.2c6827d",
109
+ "@dxos/random": "0.8.4-main.2c6827d",
110
+ "@dxos/plugin-search": "0.8.4-main.2c6827d",
111
+ "@dxos/react-ui": "0.8.4-main.2c6827d",
112
+ "@dxos/react-ui-theme": "0.8.4-main.2c6827d",
113
+ "@dxos/test-utils": "0.8.4-main.2c6827d",
114
+ "@dxos/storybook-utils": "0.8.4-main.2c6827d"
113
115
  },
114
116
  "peerDependencies": {
115
- "@effect-rx/rx-react": "^0.34.1",
117
+ "@effect-atom/atom-react": "^0.3.4",
116
118
  "@effect/platform": "^0.80.12",
117
119
  "effect": "^3.13.3",
118
- "react": "~18.2.0",
119
- "react-dom": "~18.2.0",
120
- "@dxos/react-ui": "0.8.4-main.28f8d3d",
121
- "@dxos/react-ui-theme": "0.8.4-main.28f8d3d"
120
+ "react": "^19.0.0",
121
+ "react-dom": "^19.0.0",
122
+ "@dxos/react-ui": "0.8.4-main.2c6827d",
123
+ "@dxos/react-ui-theme": "0.8.4-main.2c6827d"
122
124
  },
123
125
  "publishConfig": {
124
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';
@@ -57,15 +55,20 @@ const DynamicTableStory = () => {
57
55
  // Story definitions.
58
56
  //
59
57
 
60
- const meta: Meta = {
58
+ const meta = {
61
59
  title: 'ui/react-ui-table/DynamicTable',
62
60
  component: DynamicTable,
63
- parameters: { translations },
64
- decorators: [withLayout({ fullscreen: true }), withTheme],
65
- };
61
+ decorators: [withTheme],
62
+ parameters: {
63
+ layout: 'fullscreen',
64
+ translations,
65
+ },
66
+ } satisfies Meta<typeof DynamicTable>;
66
67
 
67
68
  export default meta;
68
69
 
70
+ type Story = StoryObj<typeof meta>;
71
+
69
72
  export const Default: StoryObj = {
70
73
  render: DynamicTableStory,
71
74
  };
@@ -129,7 +132,7 @@ export const WithEchoSchema: StoryObj = {
129
132
  render: () => {
130
133
  const client = useClient();
131
134
  const { space } = useClientProvider();
132
- const schema = useSchema(client, space, Testing.Contact.typename);
135
+ const schema = useSchema(client, space, Testing.Person.typename);
133
136
  const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
134
137
  if (!schema) {
135
138
  return <div>Loading schema...</div>;
@@ -139,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
139
142
  },
140
143
  decorators: [
141
144
  withClientProvider({
142
- types: [Testing.Contact],
145
+ types: [Testing.Person],
143
146
  createIdentity: true,
144
147
  createSpace: true,
145
- onSpaceCreated: async ({ space }) => {
148
+ onCreateSpace: async ({ space }) => {
146
149
  Array.from({ length: 10 }).forEach(() => {
147
150
  space.db.add(
148
- Obj.make(Testing.Contact, {
151
+ Obj.make(Testing.Person, {
149
152
  name: faker.person.fullName(),
150
153
  email: faker.internet.email(),
151
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,27 +2,31 @@
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
- import { DataType } from '@dxos/schema';
17
+ import { useAsyncEffect } from '@dxos/react-ui';
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';
16
21
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
17
- import { withLayout, withTheme } from '@dxos/storybook-utils';
22
+ import { Organization, Person } from '@dxos/types';
18
23
 
19
24
  import { useTableModel } from '../../hooks';
20
25
  import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
21
26
  import { translations } from '../../translations';
22
- import { TableView } from '../../types';
23
- import { createTable } from '../../util';
27
+ import { Table } from '../../types';
24
28
 
25
- import { Table } from './Table';
29
+ import { Table as TableComponent } from './Table';
26
30
 
27
31
  faker.seed(1);
28
32
  const generator: ValueGenerator = faker as any;
@@ -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>(
@@ -42,23 +46,20 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
42
46
  [],
43
47
  );
44
48
 
45
- useEffect(() => {
49
+ useAsyncEffect(async () => {
46
50
  if (!space) {
47
51
  return;
48
52
  }
49
53
 
50
- const timeout = setTimeout(async () => {
51
- const {
52
- jsonSchema,
53
- schema: effectSchema,
54
- view,
55
- } = await createTable({ client, space, typename: Type.getTypename(schema) });
56
- setJsonSchema(jsonSchema);
57
- setView(view);
58
- space.db.add(view);
59
- await space.db.schemaRegistry.register([effectSchema]);
60
- });
61
- return () => clearTimeout(timeout);
54
+ const {
55
+ jsonSchema,
56
+ schema: effectSchema,
57
+ view,
58
+ } = await Table.makeView({ client, space, typename: Type.getTypename(schema) });
59
+ setJsonSchema(jsonSchema);
60
+ setView(view);
61
+ space.db.add(view);
62
+ await space.db.schemaRegistry.register([effectSchema]);
62
63
  }, [client, space, schema]);
63
64
 
64
65
  const model = useTableModel<TableRow>({ view, schema: jsonSchema, rows: [], features });
@@ -87,38 +88,155 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
87
88
 
88
89
  const DefaultStory = () => {
89
90
  const client = useClient();
90
- const { model: orgModel, presentation: orgPresentation } = useTestModel(DataType.Organization, 50);
91
- 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
+ );
92
101
 
93
102
  return (
94
103
  <div className='is-full bs-full grid grid-cols-2 divide-x divide-separator'>
95
- <Table.Root>
96
- <Table.Main model={orgModel} presentation={orgPresentation} client={client} ignoreAttention />
97
- </Table.Root>
98
- <Table.Root>
99
- <Table.Main model={contactModel} presentation={contactPresentation} client={client} ignoreAttention />
100
- </Table.Root>
104
+ <TableComponent.Root>
105
+ <TableComponent.Main
106
+ model={orgModel}
107
+ schema={Organization.Organization}
108
+ presentation={orgPresentation}
109
+ onCreate={handleCreate}
110
+ client={client}
111
+ ignoreAttention
112
+ testId='relations-0'
113
+ />
114
+ </TableComponent.Root>
115
+ <TableComponent.Root>
116
+ <TableComponent.Main
117
+ model={contactModel}
118
+ schema={Person.Person}
119
+ presentation={contactPresentation}
120
+ onCreate={handleCreate}
121
+ client={client}
122
+ ignoreAttention
123
+ testId='relations-1'
124
+ />
125
+ </TableComponent.Root>
101
126
  </div>
102
127
  );
103
128
  };
104
129
 
105
- const meta: Meta<typeof DefaultStory> = {
106
- title: 'ui/react-ui-table/relations',
130
+ const meta = {
131
+ title: 'ui/react-ui-table/Relations',
107
132
  render: DefaultStory,
108
- parameters: { translations, controls: { disable: true } },
109
133
  decorators: [
134
+ withTheme,
135
+ // TODO(thure): Shouldn’t `layout: 'fullscreen'` below make this unnecessary?
136
+ withLayout({ classNames: 'fixed inset-0' }),
110
137
  withClientProvider({
111
- types: [DataType.View, DataType.Organization, DataType.Person, TableView],
138
+ types: [View.View, Organization.Organization, Person.Person, Table.Table],
112
139
  createIdentity: true,
113
140
  createSpace: true,
114
141
  }),
115
- withTheme,
116
- withLayout({ fullscreen: true }),
117
142
  ],
118
- };
143
+ parameters: {
144
+ layout: 'fullscreen',
145
+ controls: { disable: true },
146
+ translations: [...translations, ...formTranslations],
147
+ },
148
+ } satisfies Meta<typeof DefaultStory>;
119
149
 
120
150
  export default meta;
121
151
 
122
- type Story = StoryObj<typeof DefaultStory>;
152
+ type Story = StoryObj<typeof meta>;
153
+
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);
123
238
 
124
- export const Default: Story = {};
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>