@dxos/react-ui-table 0.8.4-main.72ec0f3 → 0.8.4-main.765dc60934

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 (166) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/chunk-4URYOFDL.mjs +69 -0
  3. package/dist/lib/browser/chunk-4URYOFDL.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  5. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-GS2UNGJK.mjs → chunk-YDXPC7HA.mjs} +642 -722
  7. package/dist/lib/browser/chunk-YDXPC7HA.mjs.map +7 -0
  8. package/dist/lib/browser/index.mjs +1111 -1023
  9. package/dist/lib/browser/index.mjs.map +4 -4
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/testing/index.mjs +35 -45
  12. package/dist/lib/browser/testing/index.mjs.map +3 -3
  13. package/dist/lib/browser/translations.mjs +36 -0
  14. package/dist/lib/browser/translations.mjs.map +7 -0
  15. package/dist/lib/browser/types/index.mjs +4 -3
  16. package/dist/lib/node-esm/{chunk-TRR3C5NX.mjs → chunk-2AN3QQ3D.mjs} +642 -721
  17. package/dist/lib/node-esm/chunk-2AN3QQ3D.mjs.map +7 -0
  18. package/dist/lib/node-esm/chunk-B2SZYAEC.mjs +70 -0
  19. package/dist/lib/node-esm/chunk-B2SZYAEC.mjs.map +7 -0
  20. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  21. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  22. package/dist/lib/node-esm/index.mjs +1111 -1023
  23. package/dist/lib/node-esm/index.mjs.map +4 -4
  24. package/dist/lib/node-esm/meta.json +1 -1
  25. package/dist/lib/node-esm/testing/index.mjs +35 -45
  26. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  27. package/dist/lib/node-esm/translations.mjs +37 -0
  28. package/dist/lib/node-esm/translations.mjs.map +7 -0
  29. package/dist/lib/node-esm/types/index.mjs +4 -3
  30. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
  31. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  32. package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
  33. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  34. package/dist/types/src/components/Table/DynamicTable.d.ts +6 -7
  35. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  36. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +21 -37
  37. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/Table/Relations.stories.d.ts +84 -80
  39. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  40. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  41. package/dist/types/src/components/Table/Table.d.ts +20 -21
  42. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  43. package/dist/types/src/components/Table/Table.stories.d.ts +92 -26
  44. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/Table/TableContent.d.ts +17 -0
  46. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  47. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  48. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  49. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
  50. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
  52. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
  54. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  55. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +24 -26
  56. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  57. package/dist/types/src/components/index.d.ts +0 -1
  58. package/dist/types/src/components/index.d.ts.map +1 -1
  59. package/dist/types/src/components/testing.d.ts +22 -15
  60. package/dist/types/src/components/testing.d.ts.map +1 -1
  61. package/dist/types/src/hooks/index.d.ts +2 -1
  62. package/dist/types/src/hooks/index.d.ts.map +1 -1
  63. package/dist/types/src/hooks/useAddRow.d.ts +5 -6
  64. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  65. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  66. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  67. package/dist/types/src/hooks/useTableModel.d.ts +9 -9
  68. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  69. package/dist/types/src/index.d.ts +0 -2
  70. package/dist/types/src/index.d.ts.map +1 -1
  71. package/dist/types/src/model/modal-controller.d.ts +5 -1
  72. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  73. package/dist/types/src/model/selection-model.d.ts +18 -9
  74. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  75. package/dist/types/src/model/table-model.d.ts +103 -24
  76. package/dist/types/src/model/table-model.d.ts.map +1 -1
  77. package/dist/types/src/model/table-presentation.d.ts +3 -1
  78. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  79. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  80. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  81. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  82. package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
  83. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  84. package/dist/types/src/translations.d.ts +21 -23
  85. package/dist/types/src/translations.d.ts.map +1 -1
  86. package/dist/types/src/types/Table.d.ts +25 -0
  87. package/dist/types/src/types/Table.d.ts.map +1 -0
  88. package/dist/types/src/types/index.d.ts +1 -1
  89. package/dist/types/src/types/index.d.ts.map +1 -1
  90. package/dist/types/src/util/dynamic-table.d.ts +14 -11
  91. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  92. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  93. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  94. package/dist/types/src/util/index.d.ts +2 -0
  95. package/dist/types/src/util/index.d.ts.map +1 -1
  96. package/dist/types/src/util/query-ast.d.ts +7 -0
  97. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  98. package/dist/types/src/util/schema.d.ts.map +1 -1
  99. package/dist/types/src/util/signals.d.ts.map +1 -1
  100. package/dist/types/src/util/sort.d.ts +12 -0
  101. package/dist/types/src/util/sort.d.ts.map +1 -0
  102. package/dist/types/src/util/table-buttons.d.ts +20 -20
  103. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  104. package/dist/types/src/util/table-controls.d.ts +4 -4
  105. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  106. package/dist/types/src/util/tag.d.ts.map +1 -1
  107. package/dist/types/tsconfig.tsbuildinfo +1 -1
  108. package/package.json +67 -77
  109. package/src/components/Table/ColumnActionsMenu.tsx +17 -12
  110. package/src/components/Table/ColumnSettings.tsx +8 -9
  111. package/src/components/Table/DynamicTable.stories.tsx +30 -30
  112. package/src/components/Table/DynamicTable.tsx +31 -32
  113. package/src/components/Table/Relations.stories.tsx +101 -63
  114. package/src/components/Table/RowActionsMenu.tsx +6 -4
  115. package/src/components/Table/Table.stories.tsx +119 -104
  116. package/src/components/Table/Table.tsx +43 -407
  117. package/src/components/Table/TableContent.tsx +421 -0
  118. package/src/components/{TableToolbar → Table}/TableToolbar.tsx +44 -45
  119. package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
  120. package/src/components/TableCellEditor/FormCellEditor.tsx +120 -136
  121. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +22 -19
  122. package/src/components/TableCellEditor/TableCellEditor.tsx +35 -33
  123. package/src/components/index.ts +0 -1
  124. package/src/components/testing.ts +43 -17
  125. package/src/hooks/index.ts +2 -1
  126. package/src/hooks/useAddRow.ts +8 -10
  127. package/src/hooks/useProjectionModel.ts +39 -0
  128. package/src/hooks/useTableModel.ts +36 -34
  129. package/src/index.ts +0 -4
  130. package/src/model/modal-controller.ts +23 -12
  131. package/src/model/selection-model.ts +75 -44
  132. package/src/model/table-model.test.ts +82 -55
  133. package/src/model/table-model.ts +429 -167
  134. package/src/model/table-presentation.test.ts +59 -50
  135. package/src/model/table-presentation.ts +43 -39
  136. package/src/playwright/TableManager.ts +13 -6
  137. package/src/playwright/playwright.config.ts +1 -1
  138. package/src/playwright/smoke.spec.ts +52 -8
  139. package/src/testing/useTestTableModel.ts +49 -42
  140. package/src/translations.ts +20 -22
  141. package/src/types/Table.ts +78 -0
  142. package/src/types/index.ts +1 -1
  143. package/src/util/dynamic-table.test.ts +70 -0
  144. package/src/util/dynamic-table.ts +39 -26
  145. package/src/util/index.ts +2 -0
  146. package/src/util/query-ast.ts +22 -0
  147. package/src/util/sort.ts +90 -0
  148. package/src/util/table-buttons.ts +1 -1
  149. package/src/util/table-controls.ts +2 -2
  150. package/dist/lib/browser/chunk-2PFAPQL4.mjs +0 -66
  151. package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +0 -7
  152. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +0 -7
  153. package/dist/lib/node-esm/chunk-6POOFFYF.mjs +0 -67
  154. package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +0 -7
  155. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +0 -7
  156. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  157. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  158. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  159. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  160. package/dist/types/src/model/table-sorting.d.ts +0 -52
  161. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  162. package/dist/types/src/types/schema.d.ts +0 -28
  163. package/dist/types/src/types/schema.d.ts.map +0 -1
  164. package/src/components/TableToolbar/index.ts +0 -5
  165. package/src/model/table-sorting.ts +0 -225
  166. package/src/types/schema.ts +0 -73
@@ -2,44 +2,43 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { RegistryContext } from '@effect-atom/atom-react';
5
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
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';
7
+ import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
8
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
9
9
 
10
- import { Obj, Type } from '@dxos/echo';
11
- import { type JsonSchemaType } from '@dxos/echo/internal';
10
+ import { Obj, Type, View } from '@dxos/echo';
11
+ import { invariant } from '@dxos/invariant';
12
12
  import { type DxGrid } from '@dxos/lit-grid';
13
- import '@dxos/lit-ui/dx-tag-picker.pcss';
14
- import { faker } from '@dxos/random';
15
- import { useClient } from '@dxos/react-client';
16
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
13
+ import { random } from '@dxos/random';
14
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
17
15
  import { useAsyncEffect } from '@dxos/react-ui';
16
+ import { translations as formTranslations } from '@dxos/react-ui-form/translations';
18
17
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
19
- import { translations as formTranslations } from '@dxos/react-ui-form';
20
- import { View } from '@dxos/schema';
18
+ import { ViewModel } from '@dxos/schema';
21
19
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
20
+ import { withRegistry } from '@dxos/storybook-utils';
22
21
  import { Organization, Person } from '@dxos/types';
22
+ import '@dxos/lit-ui/dx-tag-picker.pcss';
23
+
24
+ import { translations } from '#translations';
23
25
 
24
- import { useTableModel } from '../../hooks';
26
+ import { useProjectionModel, useTableModel } from '../../hooks';
25
27
  import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
26
- import { translations } from '../../translations';
27
28
  import { Table } from '../../types';
28
-
29
29
  import { Table as TableComponent } from './Table';
30
30
 
31
- faker.seed(1);
32
- const generator: ValueGenerator = faker as any;
31
+ random.seed(1);
32
+ const generator: ValueGenerator = random as any;
33
33
 
34
34
  // TODO(burdon): Many-to-many relations.
35
35
  // TODO(burdon): Mutable and immutable views.
36
36
  // TODO(burdon): Reconcile schemas types and utils (see API PR).
37
37
  // TODO(burdon): Base type for T (with id); see ECHO API PR?
38
- const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
39
- const client = useClient();
40
- const { space } = useClientProvider();
41
- const [view, setView] = useState<View.View>();
42
- const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
38
+ const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
39
+ const registry = useContext(RegistryContext);
40
+ const { space } = useClientStory();
41
+ const [object, setObject] = useState<Table.Table>();
43
42
 
44
43
  const features = useMemo<TableFeatures>(
45
44
  () => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
@@ -51,18 +50,14 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
51
50
  return;
52
51
  }
53
52
 
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]);
63
- }, [client, space, schema]);
53
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
54
+ const object = Table.make({ view, jsonSchema });
55
+ setObject(object);
56
+ space.db.add(object);
57
+ }, [space, schema]);
64
58
 
65
- const model = useTableModel<TableRow>({ view, schema: jsonSchema, rows: [], features });
59
+ const projection = useProjectionModel(schema, object, registry);
60
+ const model = useTableModel<TableRow>({ object, projection, db: space?.db, features });
66
61
 
67
62
  useEffect(() => {
68
63
  if (!model || !space) {
@@ -80,45 +75,44 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
80
75
  return;
81
76
  }
82
77
 
83
- return new TablePresentation(model);
84
- }, [model]);
78
+ return new TablePresentation(registry, model);
79
+ }, [registry, model]);
85
80
 
86
81
  return { model, presentation };
87
82
  };
88
83
 
89
84
  const DefaultStory = () => {
90
- const client = useClient();
91
85
  const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
92
86
  const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
93
- const { space } = useClientProvider();
87
+ const { space } = useClientStory();
94
88
 
95
89
  const handleCreate = useCallback(
96
- (schema: Schema.Schema.AnyNoContext, values: any) => {
97
- return client.spaces.default.db.add(Obj.make(schema, values));
90
+ (schema: Type.AnyEntity, values: any) => {
91
+ invariant(Type.isObjectSchema(schema));
92
+ invariant(space);
93
+ return space.db.add(Obj.make(schema, values));
98
94
  },
99
95
  [space],
100
96
  );
101
97
 
102
98
  return (
103
- <div className='is-full bs-full grid grid-cols-2 divide-x divide-separator'>
99
+ <div className='dx-expander grid grid-cols-2 divide-x divide-separator'>
104
100
  <TableComponent.Root>
105
- <TableComponent.Main
101
+ <TableComponent.Content
106
102
  model={orgModel}
107
103
  schema={Organization.Organization}
108
104
  presentation={orgPresentation}
109
105
  onCreate={handleCreate}
110
- client={client}
111
106
  ignoreAttention
112
107
  testId='relations-0'
113
108
  />
114
109
  </TableComponent.Root>
115
110
  <TableComponent.Root>
116
- <TableComponent.Main
111
+ <TableComponent.Content
117
112
  model={contactModel}
118
113
  schema={Person.Person}
119
114
  presentation={contactPresentation}
120
115
  onCreate={handleCreate}
121
- client={client}
122
116
  ignoreAttention
123
117
  testId='relations-1'
124
118
  />
@@ -131,8 +125,9 @@ const meta = {
131
125
  title: 'ui/react-ui-table/Relations',
132
126
  render: DefaultStory,
133
127
  decorators: [
134
- withTheme,
135
- // TODO(thure): Shouldn’t `layout: 'fullscreen'` below make this unnecessary?
128
+ withTheme(),
129
+ withRegistry,
130
+ // TODO(thure): Shouldn't `layout: 'fullscreen'` below make this unnecessary?
136
131
  withLayout({ classNames: 'fixed inset-0' }),
137
132
  withClientProvider({
138
133
  types: [View.View, Organization.Organization, Person.Person, Table.Table],
@@ -152,7 +147,19 @@ export default meta;
152
147
  type Story = StoryObj<typeof meta>;
153
148
 
154
149
  export const Default: Story = {
155
- play: async ({ canvasElement }: any) => {
150
+ // The play function exercises a deep timing race in dx-grid + React render
151
+ // pipeline (cell focus → keydown → `dispatchEditRequest` → `setEditing` →
152
+ // `FormCellEditor` `useEffect` → `Popover` mount). On chromium browser
153
+ // tests the combobox sometimes fails to render in time; local pass rate
154
+ // ~25%, which makes pure CI re-run lottery a poor fit. Excluding from
155
+ // automated test runs (the story still renders in Storybook for visual
156
+ // review and the production code path is covered by
157
+ // `playwright/smoke.spec.ts`). Re-enable once the underlying race is
158
+ // fixed — likely needs `dispatchEditRequest` to be synchronous from
159
+ // keydown and `FormCellEditor` to render the popover off `contextEditing`
160
+ // directly instead of mirroring it through local state.
161
+ tags: ['!test'],
162
+ play: async ({ canvasElement }) => {
156
163
  const canvas = within(canvasElement);
157
164
  const body = within(document.body);
158
165
 
@@ -163,19 +170,42 @@ export const Default: Story = {
163
170
  // Focus on the second table (Person/contacts table)
164
171
  await expect(secondGrid).toBeVisible();
165
172
 
173
+ // Wait for both grids' data generators to finish populating before
174
+ // interacting. `findByTestId` returns as soon as the cell DOM exists,
175
+ // but dx-grid's internal cell registry (used by `cellReadonly` and the
176
+ // edit-request dispatcher) is filled in lazily. If `keydown` lands
177
+ // before the registry has the row, `dispatchEditRequest` short-circuits
178
+ // and the combobox never opens — that's the "Unable to find combobox"
179
+ // flake. Wait for the first row of each grid to render non-empty
180
+ // content (a reliable proxy for "registry populated") BEFORE scrolling
181
+ // — once we scroll the second grid to column 4, dx-grid virtualizes
182
+ // columns 0-3 out of the DOM, so `grid.0.0` becomes unreachable.
183
+ await waitFor(
184
+ async () => {
185
+ const firstOrgCell = within(firstGrid).getByTestId('grid.0.0');
186
+ const orgText = (firstOrgCell.querySelector('.dx-grid__cell__content') as HTMLElement | null)?.textContent;
187
+ await expect(orgText).toBeTruthy();
188
+ const firstContactCell = within(secondGrid).getByTestId('grid.0.0');
189
+ const contactText = (firstContactCell.querySelector('.dx-grid__cell__content') as HTMLElement | null)
190
+ ?.textContent;
191
+ await expect(contactText).toBeTruthy();
192
+ },
193
+ { timeout: 10_000 },
194
+ );
195
+
166
196
  // Scroll to the relations column (column 4) - this mimics the scrollToColumn call
167
197
  (secondGrid.closest('dx-grid') as DxGrid).scrollToColumn(4);
168
198
 
169
- // Find and click the target cell (first row, relations column)
199
+ // Find the target cell (first row, relations column).
170
200
  const targetCell = await within(secondGrid).findByTestId('grid.4.0', undefined, { timeout: 10_000 });
171
201
  await expect(targetCell).toBeVisible();
172
202
 
173
- // Click to focus the cell
203
+ // Click to focus the cell.
174
204
  await userEvent.click(targetCell as HTMLElement);
175
205
  await expect(targetCell).toHaveFocus();
176
206
  await userEvent.keyboard('{Enter}');
177
207
 
178
- // Look for the combobox that should appear in edit mode
208
+ // Look for the combobox that should appear in edit mode.
179
209
  const combobox = await body.findByRole('combobox', undefined, { timeout: 5000 });
180
210
  await userEvent.click(combobox);
181
211
 
@@ -200,8 +230,15 @@ export const Default: Story = {
200
230
  const saveButton = await body.findByTestId('save-button');
201
231
  await userEvent.click(saveButton);
202
232
 
203
- // Verify the relation was set (cell should now contain the org name)
204
- await expect(targetCell).toHaveTextContent(orgName.substring(0, 4));
233
+ // Verify the relation was set (cell should now contain the org name).
234
+ // Wait for the grid (Lit web component) to repaint after the reactive data update.
235
+ await waitFor(
236
+ async () => {
237
+ const updatedCell = within(secondGrid).getByTestId('grid.4.0');
238
+ await expect(updatedCell).toHaveTextContent(orgName.substring(0, 4));
239
+ },
240
+ { timeout: 5000 },
241
+ );
205
242
 
206
243
  // Test object creation (new relations) - equivalent to "new relations work as expected" test
207
244
  // Find a different cell to test object creation (second row, relations column)
@@ -220,23 +257,24 @@ export const Default: Story = {
220
257
  const newSearchField = await body.findByPlaceholderText('Search…');
221
258
  await userEvent.click(newSearchField);
222
259
 
223
- // Type a new object name (this will create a new object)
260
+ // Type a new object name (this will create a new object).
224
261
  const newOrgName = 'Salieri LLC';
225
262
  await userEvent.type(newSearchField, newOrgName);
226
263
 
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}');
264
+ // Click the create option directly to open the create form.
265
+ const createOptionLabel = await body.findByText(/Create new object/, undefined, { timeout: 2000 });
266
+ await userEvent.click(createOptionLabel.closest('[role="option"]') as HTMLElement);
233
267
 
234
- // Look for and click save button
235
- const createReferencedObjectForm = await body.findByTestId('create-referenced-object-form');
268
+ // Look for and click save button.
269
+ const createReferencedObjectForm = await body.findByTestId('create-referenced-object-form', undefined, {
270
+ timeout: 2000,
271
+ });
236
272
  const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
273
+ await expect(saveObjectButton).not.toBeDisabled();
237
274
  await userEvent.click(saveObjectButton);
238
275
 
239
- // Verify the new object was created and relation was set
240
- await expect(newTargetCell).toHaveTextContent(newOrgName);
276
+ // Verify the relation was set by checking for the accessory link anchor in the cell.
277
+ const updatedNewCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 5000 });
278
+ await expect(updatedNewCell.querySelector('dx-anchor')).toBeTruthy();
241
279
  },
242
- } as any;
280
+ };
@@ -2,19 +2,21 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { useAtomValue } from '@effect-atom/atom-react';
5
6
  import React from 'react';
6
7
 
7
8
  import { DropdownMenu, toLocalizedString, useTranslation } from '@dxos/react-ui';
8
9
 
10
+ import { translationKey } from '#translations';
11
+
9
12
  import { type ModalController, type TableModel } from '../../model';
10
- import { translationKey } from '../../translations';
11
13
 
12
14
  type RowActionsMenuProps = { model: TableModel; modals: ModalController };
13
15
 
14
16
  export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
15
17
  const { t } = useTranslation(translationKey);
16
- const hasSelection = model.selection.hasSelection.value;
17
- const state = modals.state.value;
18
+ const hasSelection = model.selection.hasSelection;
19
+ const state = useAtomValue(modals.state);
18
20
  if (state?.type !== 'row') {
19
21
  return null;
20
22
  }
@@ -46,7 +48,7 @@ export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
46
48
  {/* Default actions */}
47
49
  {model.features.dataEditable !== false && (
48
50
  <DropdownMenu.Item data-testid='row-menu-delete' onClick={() => model.deleteRow(state.rowIndex)}>
49
- {t(hasSelection ? 'bulk delete row label' : 'delete row label')}
51
+ {t(hasSelection ? 'bulk-delete-row.label' : 'delete-row.label')}
50
52
  </DropdownMenu.Item>
51
53
  )}
52
54
  </DropdownMenu.Viewport>