@dxos/react-ui-table 0.8.4-main.9735255 → 0.8.4-main.abd8ff62ef

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 (137) hide show
  1. package/dist/lib/browser/{chunk-OZBLY7XM.mjs → chunk-FBURZBPH.mjs} +17 -23
  2. package/dist/lib/browser/chunk-FBURZBPH.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  4. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-YGC7LZJN.mjs} +39 -90
  6. package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +198 -235
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/testing/index.mjs +7 -6
  11. package/dist/lib/browser/testing/index.mjs.map +3 -3
  12. package/dist/lib/browser/translations.mjs +36 -0
  13. package/dist/lib/browser/translations.mjs.map +7 -0
  14. package/dist/lib/browser/types/index.mjs +2 -1
  15. package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-DKKMSVKP.mjs} +17 -23
  16. package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
  17. package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-GKIWDEZ7.mjs} +39 -90
  18. package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs.map +7 -0
  19. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  20. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  21. package/dist/lib/node-esm/index.mjs +198 -235
  22. package/dist/lib/node-esm/index.mjs.map +4 -4
  23. package/dist/lib/node-esm/meta.json +1 -1
  24. package/dist/lib/node-esm/testing/index.mjs +7 -6
  25. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  26. package/dist/lib/node-esm/translations.mjs +37 -0
  27. package/dist/lib/node-esm/translations.mjs.map +7 -0
  28. package/dist/lib/node-esm/types/index.mjs +2 -1
  29. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
  32. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +20 -22
  34. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/Relations.stories.d.ts +82 -82
  36. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  38. package/dist/types/src/components/Table/Table.d.ts +20 -21
  39. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  40. package/dist/types/src/components/Table/Table.stories.d.ts +84 -84
  41. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  42. package/dist/types/src/components/Table/TableContent.d.ts +17 -0
  43. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  44. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  45. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  46. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  47. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
  48. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  49. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
  50. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +22 -24
  52. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  53. package/dist/types/src/components/index.d.ts +0 -1
  54. package/dist/types/src/components/index.d.ts.map +1 -1
  55. package/dist/types/src/components/testing.d.ts.map +1 -1
  56. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  57. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
  59. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  60. package/dist/types/src/hooks/useTableModel.d.ts +1 -1
  61. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  62. package/dist/types/src/index.d.ts +0 -2
  63. package/dist/types/src/index.d.ts.map +1 -1
  64. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  65. package/dist/types/src/model/selection-model.d.ts +1 -1
  66. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  67. package/dist/types/src/model/table-model.d.ts +7 -6
  68. package/dist/types/src/model/table-model.d.ts.map +1 -1
  69. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  70. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  71. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  72. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  73. package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
  74. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  75. package/dist/types/src/translations.d.ts +19 -21
  76. package/dist/types/src/translations.d.ts.map +1 -1
  77. package/dist/types/src/types/Table.d.ts +1 -6
  78. package/dist/types/src/types/Table.d.ts.map +1 -1
  79. package/dist/types/src/util/dynamic-table.d.ts +6 -5
  80. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  81. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  82. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  83. package/dist/types/src/util/query-ast.d.ts.map +1 -1
  84. package/dist/types/src/util/schema.d.ts.map +1 -1
  85. package/dist/types/src/util/signals.d.ts.map +1 -1
  86. package/dist/types/src/util/sort.d.ts.map +1 -1
  87. package/dist/types/src/util/table-buttons.d.ts +20 -20
  88. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  89. package/dist/types/src/util/table-controls.d.ts +4 -4
  90. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  91. package/dist/types/src/util/tag.d.ts.map +1 -1
  92. package/dist/types/tsconfig.tsbuildinfo +1 -1
  93. package/package.json +59 -62
  94. package/src/components/Table/ColumnActionsMenu.tsx +7 -6
  95. package/src/components/Table/ColumnSettings.tsx +3 -4
  96. package/src/components/Table/DynamicTable.stories.tsx +19 -18
  97. package/src/components/Table/DynamicTable.tsx +13 -20
  98. package/src/components/Table/Relations.stories.tsx +78 -34
  99. package/src/components/Table/RowActionsMenu.tsx +3 -2
  100. package/src/components/Table/Table.stories.tsx +72 -52
  101. package/src/components/Table/Table.tsx +41 -435
  102. package/src/components/Table/TableContent.tsx +421 -0
  103. package/src/components/{TableToolbar → Table}/TableToolbar.tsx +36 -43
  104. package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
  105. package/src/components/TableCellEditor/FormCellEditor.tsx +14 -7
  106. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
  107. package/src/components/TableCellEditor/TableCellEditor.tsx +12 -10
  108. package/src/components/index.ts +0 -1
  109. package/src/components/testing.ts +10 -10
  110. package/src/hooks/useAddRow.ts +1 -1
  111. package/src/hooks/useProjectionModel.ts +3 -3
  112. package/src/hooks/useTableModel.ts +1 -1
  113. package/src/index.ts +0 -4
  114. package/src/model/table-model.test.ts +5 -6
  115. package/src/model/table-model.ts +18 -17
  116. package/src/model/table-presentation.test.ts +13 -7
  117. package/src/model/table-presentation.ts +12 -12
  118. package/src/playwright/TableManager.ts +10 -3
  119. package/src/playwright/playwright.config.ts +1 -1
  120. package/src/playwright/smoke.spec.ts +48 -5
  121. package/src/testing/useTestTableModel.ts +6 -6
  122. package/src/translations.ts +19 -21
  123. package/src/types/Table.ts +12 -23
  124. package/src/util/dynamic-table.test.ts +77 -0
  125. package/src/util/dynamic-table.ts +17 -16
  126. package/src/util/query-ast.ts +3 -0
  127. package/src/util/table-buttons.ts +1 -1
  128. package/src/util/table-controls.ts +2 -2
  129. package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
  130. package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
  131. package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
  132. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
  133. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  134. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  135. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  136. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  137. package/src/components/TableToolbar/index.ts +0 -5
@@ -5,7 +5,7 @@
5
5
  import { expect, test } from '@playwright/test';
6
6
 
7
7
  import { type DxGrid } from '@dxos/lit-grid';
8
- import { faker } from '@dxos/random';
8
+ import { random } from '@dxos/random';
9
9
  import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
10
10
 
11
11
  import { TableManager } from './TableManager';
@@ -13,7 +13,7 @@ import { TableManager } from './TableManager';
13
13
  const storyUrl = storybookUrl('ui-react-ui-table-table--default', 9004);
14
14
  const relationsStoryUrl = storybookUrl('ui-react-ui-table-relations--default', 9004);
15
15
 
16
- // NOTE(ZaymonFC): This test suite relies on the faker seed being set to 0 in the story.
16
+ // NOTE(ZaymonFC): This test suite relies on the random seed being set to 0 in the story.
17
17
  test.describe('Table', () => {
18
18
  test('Loads', async ({ browser, browserName }) => {
19
19
  test.skip(browserName === 'webkit');
@@ -121,8 +121,22 @@ test.describe('Table', () => {
121
121
  await page.close();
122
122
  });
123
123
 
124
- // Rest of add column test remains the same as it's a more complex flow.
125
- test('add column', async ({ browser, browserName }) => {
124
+ test('add column without changing format', async ({ browser, browserName }) => {
125
+ test.skip(browserName === 'webkit');
126
+ test.skip(browserName === 'firefox');
127
+ const { page } = await setupPage(browser, { url: storyUrl });
128
+ const table = new TableManager(page);
129
+
130
+ await table.grid.ready();
131
+ const newColumnLabel = 'DEFAULT FORMAT COL';
132
+
133
+ await table.addColumn({ label: newColumnLabel });
134
+
135
+ await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
136
+ await page.close();
137
+ });
138
+
139
+ test('add column with format', async ({ browser, browserName }) => {
126
140
  test.skip(browserName === 'webkit');
127
141
  test.skip(browserName === 'firefox');
128
142
  const { page } = await setupPage(browser, { url: storyUrl });
@@ -160,6 +174,35 @@ test.describe('Table', () => {
160
174
  await page.close();
161
175
  });
162
176
 
177
+ test('add row and edit cell', async ({ browser, browserName }) => {
178
+ test.skip(browserName === 'webkit');
179
+ test.skip(browserName === 'firefox');
180
+ const { page } = await setupPage(browser, { url: storyUrl });
181
+ const table = new TableManager(page);
182
+
183
+ await table.grid.ready();
184
+
185
+ // Click the CTA row at the bottom to add a new row.
186
+ await table.grid.cell(0, 0, 'frozenRowsEnd').click();
187
+
188
+ // All schema fields are optional so the row is added directly to the grid (not as a draft).
189
+ const newCell = table.grid.cell(0, 10, 'grid');
190
+ await newCell.waitFor({ state: 'visible' });
191
+
192
+ // Focus should already be on the new row; engage edit mode.
193
+ await page.keyboard.press('Enter');
194
+ await page.getByTestId('grid.cell-editor').waitFor({ state: 'visible' });
195
+
196
+ // Type text and confirm.
197
+ const cellContent = 'New row content';
198
+ await page.keyboard.type(cellContent);
199
+ await page.keyboard.press('Enter');
200
+
201
+ // Assert the cell content is saved.
202
+ await expect(newCell).toHaveText(cellContent);
203
+ await page.close();
204
+ });
205
+
163
206
  // TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
164
207
  test.skip('extant relations work as expected', async ({ browser, browserName }) => {
165
208
  test.skip(browserName === 'webkit');
@@ -267,7 +310,7 @@ test.describe('Table', () => {
267
310
  dxGridElement.scrollToColumn(6);
268
311
  });
269
312
 
270
- const nonRefContent = faker.lorem.words(3);
313
+ const nonRefContent = random.lorem.words(3);
271
314
  const nonRefCell = dxGrid.locator('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="6"]');
272
315
  await nonRefCell.click();
273
316
  await page.keyboard.press('Enter');
@@ -7,10 +7,10 @@ import { type RefObject, useCallback, useContext, useMemo, useRef } from 'react'
7
7
 
8
8
  import { type Database, type Type } from '@dxos/echo';
9
9
  import { isMutable } from '@dxos/echo/internal';
10
- import { faker } from '@dxos/random';
10
+ import { random } from '@dxos/random';
11
11
  import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
12
12
  import { useClientStory } from '@dxos/react-client/testing';
13
- import { useGlobalFilteredObjects } from '@dxos/react-ui-searchlist';
13
+ import { useGlobalFilteredObjects } from '@dxos/react-ui-search';
14
14
  import { type ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
15
15
 
16
16
  import { type TableController } from '../components';
@@ -18,9 +18,9 @@ import { useAddRow, useProjectionModel, useTableModel } from '../hooks';
18
18
  import { type TableModel, TablePresentation } from '../model';
19
19
  import { Table } from '../types';
20
20
 
21
- faker.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
21
+ random.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
22
22
 
23
- export type TestTableModel<T extends Type.Obj.Any = Type.Obj.Any> = {
23
+ export type TestTableModel<T extends Type.AnyObj = Type.AnyObj> = {
24
24
  schema: T | undefined;
25
25
  table: Table.Table | undefined;
26
26
  projection: ProjectionModel | undefined;
@@ -38,7 +38,7 @@ export type TestTableModel<T extends Type.Obj.Any = Type.Obj.Any> = {
38
38
  * Custom hook to create and manage a test table model for storybook demonstrations.
39
39
  * Provides table data, schema, and handlers for table operations.
40
40
  */
41
- export const useTestTableModel = <T extends Type.Obj.Any = Type.Obj.Any>(): TestTableModel<T> => {
41
+ export const useTestTableModel = <T extends Type.AnyObj = Type.AnyObj>(): TestTableModel<T> => {
42
42
  const registry = useContext(RegistryContext);
43
43
  const { space } = useClientStory();
44
44
  const db = space?.db;
@@ -97,7 +97,7 @@ export const useTestTableModel = <T extends Type.Obj.Any = Type.Obj.Any>(): Test
97
97
  rows: filteredObjects,
98
98
  onInsertRow: addRow,
99
99
  onDeleteRows: handleDeleteRows,
100
- onDeleteColumn: handleDeleteColumn,
100
+ onColumnDelete: handleDeleteColumn,
101
101
  onCellUpdate: handleCellUpdate,
102
102
  onRowOrderChange: handleRowOrderChange,
103
103
  });
@@ -10,27 +10,25 @@ export const translations = [
10
10
  {
11
11
  'en-US': {
12
12
  [translationKey]: {
13
- 'object placeholder': 'New table',
14
- 'create object label': 'Create table',
15
- 'table name placeholder': 'Table name',
16
- 'settings title': 'Table settings',
17
- 'table schema label': 'Type',
18
- 'new schema': 'New type',
19
- 'continue label': 'Continue',
20
- 'create stack section label': 'Create table',
21
- 'add row label': 'Add row',
22
- 'create comment': 'Create thread',
23
- 'column action sort descending': 'Sort Descending',
24
- 'column action sort ascending': 'Sort Ascending',
25
- 'column action clear sorting': 'Clear Sorting',
26
- 'column action settings': 'Column Settings',
27
- 'column action delete': 'Delete Column',
28
- 'delete row label': 'Delete row',
29
- 'bulk delete row label': 'Delete selected rows',
30
- 'column deleted label': 'Column deleted',
31
- 'new column button label': 'Create column',
32
- 'create new object label': 'Create new object “{{text}}”',
33
- 'save view label': 'Save changes to view',
13
+ 'object.placeholder': 'New table',
14
+ 'create-object.label': 'Create table',
15
+ 'table-name.placeholder': 'Table name',
16
+ 'settings.title': 'Table settings',
17
+ 'table-schema.label': 'Type',
18
+ 'continue.label': 'Continue',
19
+ 'create-stack-section.label': 'Create table',
20
+ 'add-row.label': 'Add row',
21
+ 'column-action-sort-descending.menu': 'Sort Descending',
22
+ 'column-action-sort-ascending.menu': 'Sort Ascending',
23
+ 'column-action-clear-sorting.menu': 'Clear Sorting',
24
+ 'column-action-settings.menu': 'Column Settings',
25
+ 'column-action-delete.menu': 'Delete Column',
26
+ 'delete-row.label': 'Delete row',
27
+ 'bulk-delete-row.label': 'Delete selected rows',
28
+ 'column-deleted.label': 'Column deleted',
29
+ 'new-column-button.label': 'Create column',
30
+ 'create-new-object.label': 'Create new object “{{text}}”',
31
+ 'save-view.label': 'Save changes to view',
34
32
  },
35
33
  },
36
34
  },
@@ -6,14 +6,15 @@ import * as Match from 'effect/Match';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import * as SchemaAST from 'effect/SchemaAST';
8
8
 
9
- import { Obj, Ref, Type } from '@dxos/echo';
9
+ import { Annotation, JsonSchema, Obj, Ref, Type } from '@dxos/echo';
10
+ import { View } from '@dxos/echo';
10
11
  import { FormInputAnnotation, type JsonPath, type JsonSchemaType, LabelAnnotation } from '@dxos/echo/internal';
11
- import { View, ViewAnnotation } from '@dxos/schema';
12
+ import { ViewAnnotation } from '@dxos/schema';
12
13
 
13
14
  export const Table = Schema.Struct({
14
15
  name: Schema.String.pipe(Schema.optional),
15
16
 
16
- view: Type.Ref(View.View).pipe(FormInputAnnotation.set(false)),
17
+ view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false)),
17
18
 
18
19
  sizes: Schema.Record({
19
20
  // TODO(wittjosiah): Should be JsonPath.
@@ -22,11 +23,15 @@ export const Table = Schema.Struct({
22
23
  }).pipe(Schema.mutable, FormInputAnnotation.set(false)),
23
24
  }).pipe(
24
25
  Type.object({
25
- typename: 'dxos.org/type/Table',
26
- version: '0.2.0',
26
+ typename: 'org.dxos.type.table',
27
+ version: '0.1.0',
27
28
  }),
28
29
  LabelAnnotation.set(['name']),
29
- ViewAnnotation.set(true),
30
+ ViewAnnotation.set(['view']),
31
+ Annotation.IconAnnotation.set({
32
+ icon: 'ph--table--regular',
33
+ hue: 'green',
34
+ }),
30
35
  );
31
36
 
32
37
  export interface Table extends Schema.Schema.Type<typeof Table> {}
@@ -47,7 +52,7 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
47
52
 
48
53
  // Preset sizes.
49
54
  if (jsonSchema) {
50
- const schema = Type.toEffectSchema(jsonSchema);
55
+ const schema = JsonSchema.toEffectSchema(jsonSchema);
51
56
  const properties = SchemaAST.getPropertySignatures(schema.ast);
52
57
  for (const property of properties) {
53
58
  const name = property.name.toString() as JsonPath;
@@ -72,19 +77,3 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
72
77
 
73
78
  return table;
74
79
  };
75
-
76
- //
77
- // V1
78
- //
79
-
80
- export const TableV1 = Schema.Struct({
81
- sizes: Schema.Record({
82
- key: Schema.String,
83
- value: Schema.Number,
84
- }).pipe(Schema.mutable),
85
- }).pipe(
86
- Type.object({
87
- typename: 'dxos.org/type/Table',
88
- version: '0.1.0',
89
- }),
90
- );
@@ -0,0 +1,77 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Registry } from '@effect-atom/atom-react';
6
+ import { describe, expect, test } from 'vitest';
7
+
8
+ import { Format } from '@dxos/echo';
9
+
10
+ import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from './dynamic-table';
11
+
12
+ // Quarantined in CI only — flaky under the workflow's `--no-file-parallelism`
13
+ // runner / hoisted node_modules layout. Local runs still cover the suite so
14
+ // regressions surface during development.
15
+ // Trunk.io tracking:
16
+ // https://app.trunk.io/dxos/flaky-tests/test/fe424f97-f8a1-5f2f-a191-5b3ec4e8f6b7
17
+ // https://app.trunk.io/dxos/flaky-tests/test/ee00f293-9b9b-55ed-b4cd-48a07edea96c
18
+ const describeOutsideCI = process.env.CI ? describe.skip : describe;
19
+ describeOutsideCI('makeDynamicTable', () => {
20
+ /**
21
+ * Base case: plain jsonSchema (not from Echo / JsonSchema.toJsonSchema). Does not exercise the path
22
+ * where projection or schema are reactive, so this does not reproduce the Obj.update regression.
23
+ * Confirms makeDynamicTable + setProperties (title) works with plain objects.
24
+ */
25
+ test('makeDynamicTable with plain jsonSchema and properties with title does not throw', () => {
26
+ const registry = Registry.make();
27
+ const jsonSchema = {
28
+ type: 'object' as const,
29
+ properties: {
30
+ id: { type: 'string' as const },
31
+ name: { type: 'string' as const },
32
+ },
33
+ required: ['id'],
34
+ };
35
+ const properties: TablePropertyDefinition[] = [
36
+ { name: 'id', format: Format.TypeFormat.String },
37
+ { name: 'name', format: Format.TypeFormat.String, title: 'Name' },
38
+ ];
39
+
40
+ expect(() => {
41
+ const { projection, object } = makeDynamicTable({
42
+ registry,
43
+ jsonSchema,
44
+ properties,
45
+ });
46
+ expect(projection).toBeDefined();
47
+ expect(object).toBeDefined();
48
+ expect(projection.getFields().length).toBeGreaterThan(0);
49
+ }).not.toThrow();
50
+ });
51
+
52
+ /**
53
+ * Regression test for the path where jsonSchema comes from getBaseSchema(typename, properties).
54
+ * That path uses Echo (ViewModel.make, JsonSchema.toJsonSchema); mutations must run inside Obj.update and on a
55
+ * cloned schema. This test ensures that flow does not throw.
56
+ */
57
+ test('makeDynamicTable with jsonSchema from getBaseSchema(typename, properties) and properties with title does not throw', () => {
58
+ const registry = Registry.make();
59
+ const typename = 'com.example.type.dynamicTableTest';
60
+ const properties: TablePropertyDefinition[] = [
61
+ { name: 'id', format: Format.TypeFormat.String },
62
+ { name: 'name', format: Format.TypeFormat.String, title: 'Name' },
63
+ ];
64
+ const { jsonSchema } = getBaseSchema({ typename, properties });
65
+
66
+ expect(() => {
67
+ const { projection, object } = makeDynamicTable({
68
+ registry,
69
+ jsonSchema,
70
+ properties,
71
+ });
72
+ expect(projection).toBeDefined();
73
+ expect(object).toBeDefined();
74
+ expect(projection.getFields().length).toBeGreaterThan(0);
75
+ }).not.toThrow();
76
+ });
77
+ });
@@ -3,13 +3,15 @@
3
3
  //
4
4
 
5
5
  import { type Registry } from '@effect-atom/atom-react';
6
+ import type * as Types from 'effect/Types';
6
7
 
7
- import { Filter, type JsonSchema, Obj, Order, Query, type QueryAST, Ref, Type } from '@dxos/echo';
8
+ import { Filter, JsonSchema, Obj, Order, Query, type QueryAST, Ref, Type } from '@dxos/echo';
9
+ import { type View } from '@dxos/echo';
8
10
  import {
9
11
  ProjectionModel,
10
12
  type SchemaPropertyDefinition,
11
- View,
12
- createDirectChangeCallback,
13
+ ViewModel,
14
+ createEchoChangeCallback,
13
15
  getSchemaFromPropertyDefinitions,
14
16
  } from '@dxos/schema';
15
17
 
@@ -34,16 +36,16 @@ export const getBaseSchema = ({
34
36
  properties,
35
37
  jsonSchema,
36
38
  }: {
37
- schema?: Type.Entity.Any;
39
+ schema?: Type.AnyEntity;
38
40
  typename?: string;
39
41
  properties?: TablePropertyDefinition[];
40
- jsonSchema?: JsonSchema.JsonSchema;
41
- }): { typename: string; jsonSchema: JsonSchema.JsonSchema } => {
42
+ jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
43
+ }): { typename: string; jsonSchema: Types.DeepMutable<JsonSchema.JsonSchema> } => {
42
44
  if (typename && properties) {
43
45
  const schema = getSchemaFromPropertyDefinitions(typename, properties);
44
- return { typename: schema.typename, jsonSchema: schema.jsonSchema };
46
+ return { typename: schema.typename, jsonSchema: JsonSchema.toJsonSchema(schema) };
45
47
  } else if (schema) {
46
- return { typename: Type.getTypename(schema)!, jsonSchema: Type.toJsonSchema(schema) };
48
+ return { typename: Type.getTypename(schema)!, jsonSchema: JsonSchema.toJsonSchema(schema) };
47
49
  } else if (typename && jsonSchema) {
48
50
  return { typename, jsonSchema };
49
51
  } else {
@@ -57,22 +59,21 @@ export const makeDynamicTable = ({
57
59
  properties,
58
60
  }: {
59
61
  registry: Registry.Registry;
60
- jsonSchema: JsonSchema.JsonSchema;
62
+ jsonSchema: Types.DeepMutable<JsonSchema.JsonSchema>;
61
63
  properties?: TablePropertyDefinition[];
62
64
  }): { projection: ProjectionModel; object: Table.Table } => {
63
- const view = View.make({
65
+ const view = ViewModel.make({
64
66
  query: Query.select(Filter.everything()),
65
67
  jsonSchema,
66
68
  ...(properties && { fields: properties.map((property) => property.name) }),
67
69
  });
68
70
  const object = Obj.make(Table.Table, { view: Ref.make(view), sizes: {} });
69
71
 
70
- // Use direct change callback for in-memory objects (non-ECHO backed).
71
72
  const projection = new ProjectionModel({
72
73
  registry,
73
74
  view,
74
75
  baseSchema: jsonSchema,
75
- change: createDirectChangeCallback(view.projection, jsonSchema),
76
+ change: createEchoChangeCallback(view, jsonSchema),
76
77
  });
77
78
  projection.normalizeView();
78
79
  if (properties && projection.getFields()) {
@@ -92,8 +93,8 @@ const setProperties = (
92
93
  const field = projection.getFields().find((field) => field.path === property.name);
93
94
  if (field) {
94
95
  if (property.size !== undefined) {
95
- Obj.change(table, (t) => {
96
- t.sizes[field.path] = property.size!;
96
+ Obj.update(table, (table) => {
97
+ table.sizes[field.path] = property.size!;
97
98
  });
98
99
  }
99
100
 
@@ -110,9 +111,9 @@ const setProperties = (
110
111
  const currentQuery = Query.fromAst(Obj.getSnapshot(view).query.ast);
111
112
  // Use any type parameter since we're working with dynamic field paths
112
113
  const newQuery = currentQuery.orderBy(Order.property<any>(field.path as string, property.sort));
113
- Obj.change(view, (v) => {
114
+ Obj.update(view, (view) => {
114
115
  // Type assertion needed because Query AST types have some variance issues.
115
- v.query.ast = newQuery.ast as typeof v.query.ast;
116
+ view.query.ast = newQuery.ast as typeof view.query.ast;
116
117
  });
117
118
  }
118
119
  }
@@ -15,5 +15,8 @@ export const extractOrder = (queryAst: QueryAST.Query): readonly QueryAST.Order[
15
15
  if (queryAst.type === 'options') {
16
16
  return extractOrder(queryAst.query);
17
17
  }
18
+ if (queryAst.type === 'from') {
19
+ return extractOrder(queryAst.query);
20
+ }
18
21
  return undefined;
19
22
  };
@@ -39,7 +39,7 @@ const createButton = ({
39
39
  .map(([k, v]) => `${k}="${v}"`)
40
40
  .join(' ');
41
41
 
42
- return `<button ${attr} data-testid="${testId}" class="dx-button is-6 aspect-square pli-0.5 min-bs-0" ${dataAttrs} ${disabled ? 'disabled' : ''} data-dx-grid-action="accessory"><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
42
+ return `<button ${attr} data-testid="${testId}" class="dx-button w-6 aspect-square px-0.5 min-h-0" ${dataAttrs} ${disabled ? 'disabled' : ''} data-dx-grid-action="accessory"><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
43
43
  };
44
44
 
45
45
  const addColumnButton = {
@@ -25,8 +25,8 @@ export const CONTROL_IDENTIFIERS = {
25
25
  } as const;
26
26
 
27
27
  const BASE_CLASSES = {
28
- checkbox: 'absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox',
29
- switch: 'absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch',
28
+ checkbox: 'absolute inset-y-[.375rem] end-[.375rem] dx-checkbox',
29
+ switch: 'absolute inset-y-[.25rem] end-[.25rem] dx-checkbox--switch',
30
30
  } as const;
31
31
 
32
32
  const renderAttributes = (data: Record<string, string>) => {