@dxos/react-ui-table 0.8.4-main.406dc2a → 0.8.4-main.52d7546f51

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 (134) hide show
  1. package/dist/lib/browser/chunk-GTGYBRJA.mjs +1748 -0
  2. package/dist/lib/browser/chunk-GTGYBRJA.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-OZBLY7XM.mjs +76 -0
  4. package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +1131 -999
  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 +30 -42
  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-DPNSZHU5.mjs +1750 -0
  12. package/dist/lib/node-esm/chunk-DPNSZHU5.mjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs +77 -0
  14. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +1131 -999
  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 +30 -42
  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/ColumnActionsMenu.d.ts +1 -2
  22. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
  24. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
  26. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +4 -18
  28. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/Table/Relations.stories.d.ts +12 -9
  30. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  32. package/dist/types/src/components/Table/Table.d.ts +8 -8
  33. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  34. package/dist/types/src/components/Table/Table.stories.d.ts +71 -5
  35. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
  37. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
  38. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  39. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
  40. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  41. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +4 -4
  42. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +4 -4
  44. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  45. package/dist/types/src/components/testing.d.ts +22 -15
  46. package/dist/types/src/components/testing.d.ts.map +1 -1
  47. package/dist/types/src/hooks/index.d.ts +2 -1
  48. package/dist/types/src/hooks/index.d.ts.map +1 -1
  49. package/dist/types/src/hooks/useAddRow.d.ts +5 -6
  50. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  51. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  52. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  53. package/dist/types/src/hooks/useTableModel.d.ts +9 -9
  54. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  55. package/dist/types/src/model/modal-controller.d.ts +5 -1
  56. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  57. package/dist/types/src/model/selection-model.d.ts +17 -8
  58. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  59. package/dist/types/src/model/table-model.d.ts +104 -25
  60. package/dist/types/src/model/table-model.d.ts.map +1 -1
  61. package/dist/types/src/model/table-presentation.d.ts +3 -1
  62. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  63. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  64. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  65. package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
  66. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  67. package/dist/types/src/translations.d.ts +4 -4
  68. package/dist/types/src/translations.d.ts.map +1 -1
  69. package/dist/types/src/types/Table.d.ts +31 -0
  70. package/dist/types/src/types/Table.d.ts.map +1 -0
  71. package/dist/types/src/types/index.d.ts +1 -1
  72. package/dist/types/src/types/index.d.ts.map +1 -1
  73. package/dist/types/src/util/dynamic-table.d.ts +14 -11
  74. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  75. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  76. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  77. package/dist/types/src/util/index.d.ts +2 -0
  78. package/dist/types/src/util/index.d.ts.map +1 -1
  79. package/dist/types/src/util/query-ast.d.ts +7 -0
  80. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  81. package/dist/types/src/util/sort.d.ts +12 -0
  82. package/dist/types/src/util/sort.d.ts.map +1 -0
  83. package/dist/types/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +53 -53
  85. package/src/components/Table/ColumnActionsMenu.tsx +10 -6
  86. package/src/components/Table/ColumnSettings.tsx +6 -6
  87. package/src/components/Table/DynamicTable.stories.tsx +15 -15
  88. package/src/components/Table/DynamicTable.tsx +24 -16
  89. package/src/components/Table/Relations.stories.tsx +43 -54
  90. package/src/components/Table/RowActionsMenu.tsx +24 -15
  91. package/src/components/Table/Table.stories.tsx +89 -80
  92. package/src/components/Table/Table.tsx +335 -305
  93. package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
  94. package/src/components/TableCellEditor/FormCellEditor.tsx +110 -133
  95. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +17 -13
  96. package/src/components/TableCellEditor/TableCellEditor.tsx +31 -31
  97. package/src/components/TableToolbar/TableToolbar.tsx +47 -31
  98. package/src/components/testing.ts +38 -12
  99. package/src/hooks/index.ts +2 -1
  100. package/src/hooks/useAddRow.ts +8 -10
  101. package/src/hooks/useProjectionModel.ts +39 -0
  102. package/src/hooks/useTableModel.ts +36 -34
  103. package/src/model/modal-controller.ts +23 -12
  104. package/src/model/selection-model.ts +75 -44
  105. package/src/model/table-model.test.ts +82 -54
  106. package/src/model/table-model.ts +435 -170
  107. package/src/model/table-presentation.test.ts +59 -49
  108. package/src/model/table-presentation.ts +32 -27
  109. package/src/playwright/TableManager.ts +13 -6
  110. package/src/playwright/playwright.config.ts +1 -1
  111. package/src/playwright/smoke.spec.ts +56 -10
  112. package/src/testing/useTestTableModel.ts +45 -38
  113. package/src/translations.ts +3 -3
  114. package/src/types/Table.ts +90 -0
  115. package/src/types/index.ts +1 -1
  116. package/src/util/dynamic-table.test.ts +70 -0
  117. package/src/util/dynamic-table.ts +40 -28
  118. package/src/util/index.ts +2 -0
  119. package/src/util/query-ast.ts +19 -0
  120. package/src/util/sort.ts +90 -0
  121. package/dist/lib/browser/chunk-4K7JPALT.mjs +0 -66
  122. package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
  123. package/dist/lib/browser/chunk-OZTXLZGM.mjs +0 -1804
  124. package/dist/lib/browser/chunk-OZTXLZGM.mjs.map +0 -7
  125. package/dist/lib/node-esm/chunk-MK6OVUF6.mjs +0 -67
  126. package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +0 -7
  127. package/dist/lib/node-esm/chunk-S56Z4V4X.mjs +0 -1805
  128. package/dist/lib/node-esm/chunk-S56Z4V4X.mjs.map +0 -7
  129. package/dist/types/src/model/table-sorting.d.ts +0 -52
  130. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  131. package/dist/types/src/types/schema.d.ts +0 -28
  132. package/dist/types/src/types/schema.d.ts.map +0 -1
  133. package/src/model/table-sorting.ts +0 -225
  134. package/src/types/schema.ts +0 -73
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.8.4-main.406dc2a",
3
+ "version": "0.8.4-main.52d7546f51",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  "./deprecated": {
@@ -56,70 +60,66 @@
56
60
  "@codemirror/autocomplete": "^6.19.0",
57
61
  "@codemirror/state": "^6.5.2",
58
62
  "@codemirror/view": "^6.38.4",
59
- "@fluentui/react-tabster": "^9.24.2",
60
- "@preact-signals/safe-react": "^0.9.0",
61
- "@preact/signals-core": "^1.12.1",
63
+ "@effect-atom/atom-react": "^0.5.0",
64
+ "@fluentui/react-tabster": "9.26.11",
62
65
  "@radix-ui/react-context": "1.1.1",
63
66
  "@radix-ui/react-primitive": "2.0.2",
64
67
  "@radix-ui/react-slot": "1.1.2",
65
68
  "@radix-ui/react-use-controllable-state": "1.1.0",
66
69
  "date-fns": "^3.3.1",
67
70
  "lodash.orderby": "^4.6.0",
68
- "@dxos/async": "0.8.4-main.406dc2a",
69
- "@dxos/debug": "0.8.4-main.406dc2a",
70
- "@dxos/context": "0.8.4-main.406dc2a",
71
- "@dxos/echo": "0.8.4-main.406dc2a",
72
- "@dxos/effect": "0.8.4-main.406dc2a",
73
- "@dxos/echo-db": "0.8.4-main.406dc2a",
74
- "@dxos/keys": "0.8.4-main.406dc2a",
75
- "@dxos/invariant": "0.8.4-main.406dc2a",
76
- "@dxos/lit-grid": "0.8.4-main.406dc2a",
77
- "@dxos/log": "0.8.4-main.406dc2a",
78
- "@dxos/plugin-search": "0.8.4-main.406dc2a",
79
- "@dxos/random": "0.8.4-main.406dc2a",
80
- "@dxos/react-ui-attention": "0.8.4-main.406dc2a",
81
- "@dxos/live-object": "0.8.4-main.406dc2a",
82
- "@dxos/react-ui-editor": "0.8.4-main.406dc2a",
83
- "@dxos/react-ui-components": "0.8.4-main.406dc2a",
84
- "@dxos/react-ui-form": "0.8.4-main.406dc2a",
85
- "@dxos/react-ui-grid": "0.8.4-main.406dc2a",
86
- "@dxos/react-ui-menu": "0.8.4-main.406dc2a",
87
- "@dxos/react-ui-searchlist": "0.8.4-main.406dc2a",
88
- "@dxos/react-ui-stack": "0.8.4-main.406dc2a",
89
- "@dxos/react-client": "0.8.4-main.406dc2a",
90
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.406dc2a",
91
- "@dxos/react-ui-types": "0.8.4-main.406dc2a",
92
- "@dxos/schema": "0.8.4-main.406dc2a",
93
- "@dxos/util": "0.8.4-main.406dc2a"
71
+ "@dxos/context": "0.8.4-main.52d7546f51",
72
+ "@dxos/debug": "0.8.4-main.52d7546f51",
73
+ "@dxos/echo": "0.8.4-main.52d7546f51",
74
+ "@dxos/effect": "0.8.4-main.52d7546f51",
75
+ "@dxos/async": "0.8.4-main.52d7546f51",
76
+ "@dxos/echo-atom": "0.8.4-main.52d7546f51",
77
+ "@dxos/invariant": "0.8.4-main.52d7546f51",
78
+ "@dxos/keys": "0.8.4-main.52d7546f51",
79
+ "@dxos/lit-grid": "0.8.4-main.52d7546f51",
80
+ "@dxos/random": "0.8.4-main.52d7546f51",
81
+ "@dxos/log": "0.8.4-main.52d7546f51",
82
+ "@dxos/react-client": "0.8.4-main.52d7546f51",
83
+ "@dxos/react-ui-components": "0.8.4-main.52d7546f51",
84
+ "@dxos/react-ui-editor": "0.8.4-main.52d7546f51",
85
+ "@dxos/react-ui-grid": "0.8.4-main.52d7546f51",
86
+ "@dxos/react-ui-menu": "0.8.4-main.52d7546f51",
87
+ "@dxos/react-ui-form": "0.8.4-main.52d7546f51",
88
+ "@dxos/react-ui-attention": "0.8.4-main.52d7546f51",
89
+ "@dxos/react-ui-searchlist": "0.8.4-main.52d7546f51",
90
+ "@dxos/react-ui-stack": "0.8.4-main.52d7546f51",
91
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.52d7546f51",
92
+ "@dxos/schema": "0.8.4-main.52d7546f51",
93
+ "@dxos/types": "0.8.4-main.52d7546f51",
94
+ "@dxos/util": "0.8.4-main.52d7546f51",
95
+ "@dxos/ui-types": "0.8.4-main.52d7546f51"
94
96
  },
95
97
  "devDependencies": {
96
- "@effect-rx/rx-react": "0.42.4",
97
- "@effect/platform": "0.92.1",
98
+ "@effect/platform": "0.94.4",
98
99
  "@types/lodash.orderby": "^4.6.9",
99
- "@types/react": "~19.2.2",
100
- "@types/react-dom": "~19.2.1",
101
- "effect": "3.18.3",
100
+ "@types/react": "~19.2.7",
101
+ "@types/react-dom": "~19.2.3",
102
+ "effect": "3.19.16",
102
103
  "fast-check": "^3.19.0",
103
- "react": "~19.2.0",
104
- "react-dom": "~19.2.0",
104
+ "react": "~19.2.3",
105
+ "react-dom": "~19.2.3",
105
106
  "vite": "7.1.9",
106
- "@dxos/echo-signals": "0.8.4-main.406dc2a",
107
- "@dxos/plugin-search": "0.8.4-main.406dc2a",
108
- "@dxos/react-ui": "0.8.4-main.406dc2a",
109
- "@dxos/react-ui-theme": "0.8.4-main.406dc2a",
110
- "@dxos/lit-ui": "0.8.4-main.406dc2a",
111
- "@dxos/random": "0.8.4-main.406dc2a",
112
- "@dxos/storybook-utils": "0.8.4-main.406dc2a",
113
- "@dxos/test-utils": "0.8.4-main.406dc2a"
107
+ "@dxos/lit-ui": "0.8.4-main.52d7546f51",
108
+ "@dxos/react-ui": "0.8.4-main.52d7546f51",
109
+ "@dxos/react-client": "0.8.4-main.52d7546f51",
110
+ "@dxos/random": "0.8.4-main.52d7546f51",
111
+ "@dxos/test-utils": "0.8.4-main.52d7546f51",
112
+ "@dxos/ui-theme": "0.8.4-main.52d7546f51",
113
+ "@dxos/storybook-utils": "0.8.4-main.52d7546f51"
114
114
  },
115
115
  "peerDependencies": {
116
- "@effect-rx/rx-react": "^0.34.1",
117
- "@effect/platform": "^0.80.12",
118
- "effect": "^3.13.3",
119
- "react": "^19.0.0",
120
- "react-dom": "^19.0.0",
121
- "@dxos/react-ui": "0.8.4-main.406dc2a",
122
- "@dxos/react-ui-theme": "0.8.4-main.406dc2a"
116
+ "@effect-atom/atom-react": "^0.5.0",
117
+ "@effect/platform": "0.94.4",
118
+ "effect": "3.19.16",
119
+ "react": "~19.2.3",
120
+ "react-dom": "~19.2.3",
121
+ "@dxos/react-ui": "0.8.4-main.52d7546f51",
122
+ "@dxos/ui-theme": "0.8.4-main.52d7546f51"
123
123
  },
124
124
  "publishConfig": {
125
125
  "access": "public"
@@ -2,6 +2,7 @@
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, useTranslation } from '@dxos/react-ui';
@@ -9,16 +10,19 @@ import { DropdownMenu, useTranslation } from '@dxos/react-ui';
9
10
  import { type ModalController, type TableModel } from '../../model';
10
11
  import { translationKey } from '../../translations';
11
12
 
12
- type ColumnActionsMenuProps = { model: TableModel; modals: ModalController };
13
+ export type ColumnActionsMenuProps = {
14
+ model: TableModel;
15
+ modals: ModalController;
16
+ };
13
17
 
14
18
  export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) => {
15
19
  const { t } = useTranslation(translationKey);
16
- const state = modals.state.value;
20
+ const state = useAtomValue(modals.state);
17
21
  if (state?.type !== 'column') {
18
22
  return null;
19
23
  }
20
24
 
21
- const currentSort = model.sorting?.sorting;
25
+ const currentSort = model.getSorting();
22
26
  const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
23
27
 
24
28
  return (
@@ -30,7 +34,7 @@ export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) =>
30
34
  {(!isCurrentColumnSorted || currentSort?.direction === 'asc') && (
31
35
  <DropdownMenu.Item
32
36
  data-testid='column-sort-descending'
33
- onClick={() => model.sorting?.setSort(state.fieldId, 'desc')}
37
+ onClick={() => model.setSort(state.fieldId, 'desc')}
34
38
  >
35
39
  {t('column action sort descending')}
36
40
  </DropdownMenu.Item>
@@ -38,13 +42,13 @@ export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) =>
38
42
  {(!isCurrentColumnSorted || currentSort?.direction === 'desc') && (
39
43
  <DropdownMenu.Item
40
44
  data-testid='column-sort-ascending'
41
- onClick={() => model.sorting?.setSort(state.fieldId, 'asc')}
45
+ onClick={() => model.setSort(state.fieldId, 'asc')}
42
46
  >
43
47
  {t('column action sort ascending')}
44
48
  </DropdownMenu.Item>
45
49
  )}
46
50
  {isCurrentColumnSorted && (
47
- <DropdownMenu.Item data-testid='column-clear-sort' onClick={() => model.sorting?.clearSort()}>
51
+ <DropdownMenu.Item data-testid='column-clear-sort' onClick={() => model.clearSort()}>
48
52
  {t('column action clear sorting')}
49
53
  </DropdownMenu.Item>
50
54
  )}
@@ -2,10 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { useAtomValue } from '@effect-atom/atom-react';
5
6
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
7
 
7
- // TODO(burdon): Remove dependency on echo-db.
8
- import { type SchemaRegistry } from '@dxos/echo-db';
8
+ import { type SchemaRegistry } from '@dxos/echo';
9
9
  import { Popover } from '@dxos/react-ui';
10
10
  import { FieldEditor } from '@dxos/react-ui-form';
11
11
  import { type FieldType } from '@dxos/schema';
@@ -13,7 +13,7 @@ import { type FieldType } from '@dxos/schema';
13
13
  import { type ModalController, type TableModel } from '../../model';
14
14
 
15
15
  type ColumnSettingsProps = {
16
- registry?: SchemaRegistry;
16
+ registry?: SchemaRegistry.SchemaRegistry;
17
17
  model?: TableModel;
18
18
  modals: ModalController;
19
19
  onNewColumn: () => void;
@@ -21,7 +21,7 @@ type ColumnSettingsProps = {
21
21
 
22
22
  export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnSettingsProps) => {
23
23
  const [newField, setNewField] = useState<FieldType>();
24
- const state = modals.state.value;
24
+ const state = useAtomValue(modals.state);
25
25
 
26
26
  useEffect(() => {
27
27
  if (state?.type === 'columnSettings' && state.mode.type === 'create' && model?.projection) {
@@ -38,11 +38,11 @@ export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnS
38
38
  if (state?.type === 'columnSettings') {
39
39
  const { mode } = state;
40
40
  if (mode.type === 'edit') {
41
- return model?.projection?.fields.find((f) => f.id === mode.fieldId);
41
+ return model?.projection?.getFields().find((f) => f.id === mode.fieldId);
42
42
  }
43
43
  }
44
44
  return undefined;
45
- }, [model?.projection?.fields, state]);
45
+ }, [model?.projection, state]);
46
46
 
47
47
  const field = existingField ?? newField;
48
48
 
@@ -3,17 +3,18 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import type * as Types from 'effect/Types';
6
7
  import React, { useMemo, useState } from 'react';
7
8
 
8
- import { Obj } from '@dxos/echo';
9
- import { FormatEnum, type JsonSchemaType } from '@dxos/echo/internal';
9
+ import { type JsonSchema, Obj } from '@dxos/echo';
10
+ import { Format } from '@dxos/echo/internal';
10
11
  import { faker } from '@dxos/random';
11
- import { useClient } from '@dxos/react-client';
12
12
  import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
13
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
13
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
14
14
  import { withTheme } from '@dxos/react-ui/testing';
15
15
  import { type SchemaPropertyDefinition } from '@dxos/schema';
16
- import { Testing } from '@dxos/schema/testing';
16
+ import { TestSchema } from '@dxos/schema/testing';
17
+ import { withRegistry } from '@dxos/storybook-utils';
17
18
 
18
19
  import { type TableFeatures } from '../../model';
19
20
  import { translations } from '../../translations';
@@ -25,8 +26,8 @@ faker.seed(0);
25
26
  const useTestPropertiesAndObjects = () => {
26
27
  const properties = useMemo<SchemaPropertyDefinition[]>(
27
28
  () => [
28
- { name: 'name', format: FormatEnum.String },
29
- { name: 'age', format: FormatEnum.Number },
29
+ { name: 'name', format: Format.TypeFormat.String },
30
+ { name: 'age', format: Format.TypeFormat.Number },
30
31
  ],
31
32
  [],
32
33
  );
@@ -58,7 +59,7 @@ const DynamicTableStory = () => {
58
59
  const meta = {
59
60
  title: 'ui/react-ui-table/DynamicTable',
60
61
  component: DynamicTable,
61
- decorators: [withTheme],
62
+ decorators: [withTheme(), withRegistry],
62
63
  parameters: {
63
64
  layout: 'fullscreen',
64
65
  translations,
@@ -101,7 +102,7 @@ export const WithClickToSelect: StoryObj = {
101
102
 
102
103
  export const WithJsonSchema: StoryObj = {
103
104
  render: () => {
104
- const schema = useMemo<JsonSchemaType>(
105
+ const schema = useMemo<Types.DeepMutable<JsonSchema.JsonSchema>>(
105
106
  () => ({
106
107
  type: 'object',
107
108
  properties: {
@@ -130,10 +131,9 @@ export const WithJsonSchema: StoryObj = {
130
131
 
131
132
  export const WithEchoSchema: StoryObj = {
132
133
  render: () => {
133
- const client = useClient();
134
- const { space } = useClientProvider();
135
- const schema = useSchema(client, space, Testing.Contact.typename);
136
- const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
134
+ const { space } = useClientStory();
135
+ const schema = useSchema(space?.db, TestSchema.Person.typename);
136
+ const objects = useQuery(space?.db, schema ? Filter.type(schema) : Filter.nothing());
137
137
  if (!schema) {
138
138
  return <div>Loading schema...</div>;
139
139
  }
@@ -142,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
142
142
  },
143
143
  decorators: [
144
144
  withClientProvider({
145
- types: [Testing.Contact],
145
+ types: [TestSchema.Person],
146
146
  createIdentity: true,
147
147
  createSpace: true,
148
148
  onCreateSpace: async ({ space }) => {
149
149
  Array.from({ length: 10 }).forEach(() => {
150
150
  space.db.add(
151
- Obj.make(Testing.Contact, {
151
+ Obj.make(TestSchema.Person, {
152
152
  name: faker.person.fullName(),
153
153
  email: faker.internet.email(),
154
154
  }),
@@ -2,25 +2,29 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import type * as Schema from 'effect/Schema';
6
- import React, { useCallback, useMemo, useRef } from 'react';
5
+ import { RegistryContext } from '@effect-atom/atom-react';
6
+ import type * as Types from 'effect/Types';
7
+ import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
7
8
 
8
- import { type JsonSchemaType } from '@dxos/echo/internal';
9
+ import { type Type } from '@dxos/echo';
10
+ import { type JsonSchema } from '@dxos/echo';
9
11
  import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
10
- import { mx } from '@dxos/react-ui-theme';
12
+ import { type ProjectionModel } from '@dxos/schema';
13
+ import { mx } from '@dxos/ui-theme';
11
14
 
12
15
  import { useTableModel } from '../../hooks';
13
16
  import { type TableFeatures, TablePresentation, type TableRowAction } from '../../model';
17
+ import { type Table as TableType } from '../../types';
14
18
  import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from '../../util';
15
19
 
16
20
  import { Table, type TableController } from './Table';
17
21
 
18
- type DynamicTableProps = ThemedClassName<{
22
+ export type DynamicTableProps<T extends Type.Entity.Any = Type.Entity.Any> = ThemedClassName<{
23
+ schema?: T;
19
24
  name?: string; // TODO(burdon): Remove?
20
25
  rows: any[];
21
26
  properties?: TablePropertyDefinition[];
22
- jsonSchema?: JsonSchemaType;
23
- schema?: Schema.Schema.AnyNoContext;
27
+ jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
24
28
  features?: Partial<TableFeatures>;
25
29
  rowActions?: TableRowAction[];
26
30
  onRowClick?: (row: any) => void;
@@ -32,25 +36,30 @@ type DynamicTableProps = ThemedClassName<{
32
36
  */
33
37
  // TODO(burdon): Instead of creating component variants, create helpers/hooks that normalize the props.
34
38
  // TODO(burdon): Warning: Cannot update a component (`DynamicTable`) while rendering a different component (`DynamicTable`).
35
- export const DynamicTable = ({
39
+ export const DynamicTable = <T extends Type.Entity.Any = Type.Entity.Any>({
36
40
  classNames,
41
+ schema,
37
42
  name = 'example.com/dynamic-table', // Rmove default or make random; this will lead to type collisions.
38
43
  rows,
39
44
  properties,
40
45
  jsonSchema: _jsonSchema,
41
- schema,
42
46
  rowActions,
43
47
  onRowClick,
44
48
  onRowAction,
45
49
  ...props
46
- }: DynamicTableProps) => {
50
+ }: DynamicTableProps<T>) => {
51
+ const registry = useContext(RegistryContext);
52
+ const [dynamicTable, setDynamicTable] = useState<{ object: TableType.Table; projection: ProjectionModel }>();
53
+
47
54
  // TODO(burdon): Remove variance from the props (should be normalized externally; possibly via hooks).
48
55
  const { jsonSchema } = useMemo(
49
56
  () => getBaseSchema({ typename: name, properties, jsonSchema: _jsonSchema, schema }),
50
57
  [name, properties, _jsonSchema, schema],
51
58
  );
52
59
 
53
- const { projection, view } = useMemo(() => makeDynamicTable({ jsonSchema, properties }), [jsonSchema, properties]);
60
+ useEffect(() => {
61
+ setDynamicTable(makeDynamicTable({ registry, jsonSchema, properties }));
62
+ }, [registry, jsonSchema, properties]);
54
63
 
55
64
  const tableRef = useRef<TableController>(null);
56
65
  const handleCellUpdate = useCallback((cell: any) => {
@@ -72,9 +81,8 @@ export const DynamicTable = ({
72
81
 
73
82
  const model = useTableModel({
74
83
  rows,
75
- view,
76
- schema: jsonSchema,
77
- projection,
84
+ object: dynamicTable?.object,
85
+ projection: dynamicTable?.projection,
78
86
  features,
79
87
  rowActions,
80
88
  onCellUpdate: handleCellUpdate,
@@ -84,9 +92,9 @@ export const DynamicTable = ({
84
92
 
85
93
  const presentation = useMemo(() => {
86
94
  if (model) {
87
- return new TablePresentation(model);
95
+ return new TablePresentation(registry, model);
88
96
  }
89
- }, [model]);
97
+ }, [registry, model]);
90
98
 
91
99
  // TODO(burdon): Do we need the outer divs?
92
100
  return (
@@ -2,25 +2,26 @@
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';
7
+ import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
8
8
  import { expect, userEvent, within } from 'storybook/test';
9
9
 
10
10
  import { Obj, Type } from '@dxos/echo';
11
- import { type JsonSchemaType } from '@dxos/echo/internal';
11
+ import { invariant } from '@dxos/invariant';
12
12
  import { type DxGrid } from '@dxos/lit-grid';
13
13
  import '@dxos/lit-ui/dx-tag-picker.pcss';
14
14
  import { faker } from '@dxos/random';
15
- import { useClient } from '@dxos/react-client';
16
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
15
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
17
16
  import { useAsyncEffect } from '@dxos/react-ui';
18
- import { withTheme } from '@dxos/react-ui/testing';
17
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
19
18
  import { translations as formTranslations } from '@dxos/react-ui-form';
20
- import { DataType } from '@dxos/schema';
19
+ import { View } from '@dxos/schema';
21
20
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
21
+ import { withRegistry } from '@dxos/storybook-utils';
22
+ import { Organization, Person } from '@dxos/types';
22
23
 
23
- import { useTableModel } from '../../hooks';
24
+ import { useProjectionModel, useTableModel } from '../../hooks';
24
25
  import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
25
26
  import { translations } from '../../translations';
26
27
  import { Table } from '../../types';
@@ -35,10 +36,9 @@ const generator: ValueGenerator = faker as any;
35
36
  // TODO(burdon): Reconcile schemas types and utils (see API PR).
36
37
  // TODO(burdon): Base type for T (with id); see ECHO API PR?
37
38
  const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
38
- const client = useClient();
39
- const { space } = useClientProvider();
40
- const [view, setView] = useState<DataType.View>();
41
- const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
39
+ const registry = useContext(RegistryContext);
40
+ const { space } = useClientStory();
41
+ const [object, setObject] = useState<Table.Table>();
42
42
 
43
43
  const features = useMemo<TableFeatures>(
44
44
  () => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
@@ -50,18 +50,14 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
50
50
  return;
51
51
  }
52
52
 
53
- const {
54
- jsonSchema,
55
- schema: effectSchema,
56
- view,
57
- } = await Table.makeView({ client, space, typename: Type.getTypename(schema) });
58
- setJsonSchema(jsonSchema);
59
- setView(view);
60
- space.db.add(view);
61
- await space.db.schemaRegistry.register([effectSchema]);
62
- }, [client, space, schema]);
53
+ const { view, jsonSchema } = await View.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]);
63
58
 
64
- 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 });
65
61
 
66
62
  useEffect(() => {
67
63
  if (!model || !space) {
@@ -79,21 +75,22 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
79
75
  return;
80
76
  }
81
77
 
82
- return new TablePresentation(model);
83
- }, [model]);
78
+ return new TablePresentation(registry, model);
79
+ }, [registry, model]);
84
80
 
85
81
  return { model, presentation };
86
82
  };
87
83
 
88
84
  const DefaultStory = () => {
89
- const client = useClient();
90
- const { model: orgModel, presentation: orgPresentation } = useTestModel(DataType.Organization, 50);
91
- const { model: contactModel, presentation: contactPresentation } = useTestModel(DataType.Person, 50);
92
- const { space } = useClientProvider();
85
+ const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
86
+ const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
87
+ const { space } = useClientStory();
93
88
 
94
89
  const handleCreate = useCallback(
95
- (schema: Schema.Schema.AnyNoContext, values: any) => {
96
- return client.spaces.default.db.add(Obj.make(schema, values));
90
+ (schema: Type.Entity.Any, values: any) => {
91
+ invariant(Type.isObjectSchema(schema));
92
+ invariant(space);
93
+ return space.db.add(Obj.make(schema, values));
97
94
  },
98
95
  [space],
99
96
  );
@@ -103,10 +100,9 @@ const DefaultStory = () => {
103
100
  <TableComponent.Root>
104
101
  <TableComponent.Main
105
102
  model={orgModel}
106
- schema={DataType.Organization}
103
+ schema={Organization.Organization}
107
104
  presentation={orgPresentation}
108
105
  onCreate={handleCreate}
109
- client={client}
110
106
  ignoreAttention
111
107
  testId='relations-0'
112
108
  />
@@ -114,10 +110,9 @@ const DefaultStory = () => {
114
110
  <TableComponent.Root>
115
111
  <TableComponent.Main
116
112
  model={contactModel}
117
- schema={DataType.Person}
113
+ schema={Person.Person}
118
114
  presentation={contactPresentation}
119
115
  onCreate={handleCreate}
120
- client={client}
121
116
  ignoreAttention
122
117
  testId='relations-1'
123
118
  />
@@ -130,9 +125,12 @@ const meta = {
130
125
  title: 'ui/react-ui-table/Relations',
131
126
  render: DefaultStory,
132
127
  decorators: [
133
- withTheme,
128
+ withTheme(),
129
+ withRegistry,
130
+ // TODO(thure): Shouldn't `layout: 'fullscreen'` below make this unnecessary?
131
+ withLayout({ classNames: 'fixed inset-0' }),
134
132
  withClientProvider({
135
- types: [DataType.View, DataType.Organization, DataType.Person, Table.Table],
133
+ types: [View.View, Organization.Organization, Person.Person, Table.Table],
136
134
  createIdentity: true,
137
135
  createSpace: true,
138
136
  }),
@@ -149,9 +147,7 @@ export default meta;
149
147
  type Story = StoryObj<typeof meta>;
150
148
 
151
149
  export const Default: Story = {
152
- // TODO(wittjosiah): This test is flaky, especially in CI.
153
- // Most typical failure is `targetCell` returns `null`.
154
- _play: async ({ canvasElement }: any) => {
150
+ play: async ({ canvasElement }: any) => {
155
151
  const canvas = within(canvasElement);
156
152
  const body = within(document.body);
157
153
 
@@ -165,11 +161,8 @@ export const Default: Story = {
165
161
  // Scroll to the relations column (column 4) - this mimics the scrollToColumn call
166
162
  (secondGrid.closest('dx-grid') as DxGrid).scrollToColumn(4);
167
163
 
168
- // Wait for scroll to complete
169
- await new Promise((resolve) => setTimeout(resolve, 1000));
170
-
171
164
  // Find and click the target cell (first row, relations column)
172
- const targetCell = secondGrid.querySelector('[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="4"]');
165
+ const targetCell = await within(secondGrid).findByTestId('grid.4.0', undefined, { timeout: 10_000 });
173
166
  await expect(targetCell).toBeVisible();
174
167
 
175
168
  // Click to focus the cell
@@ -185,11 +178,9 @@ export const Default: Story = {
185
178
  await userEvent.click(searchField);
186
179
 
187
180
  // Get the first organization name from the first table to search for
188
- const orgCell = firstGrid.querySelector(
189
- '[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="0"] .dx-grid__cell__content',
190
- ) as HTMLElement;
181
+ const orgCell = await within(firstGrid).findByTestId('grid.0.0', undefined, { timeout: 10_000 });
191
182
 
192
- const orgName = orgCell.textContent;
183
+ const orgName = (orgCell.querySelector('.dx-grid__cell__content') as HTMLElement).textContent;
193
184
  // Type the first 4 characters to search
194
185
  await userEvent.type(searchField, orgName.substring(0, 4));
195
186
 
@@ -209,9 +200,7 @@ export const Default: Story = {
209
200
 
210
201
  // Test object creation (new relations) - equivalent to "new relations work as expected" test
211
202
  // Find a different cell to test object creation (second row, relations column)
212
- const newTargetCell = secondGrid.querySelector(
213
- '[data-dx-grid-plane="grid"] [aria-rowindex="1"][aria-colindex="4"]',
214
- );
203
+ const newTargetCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 10_000 });
215
204
  await expect(newTargetCell).toBeTruthy();
216
205
 
217
206
  // Click to focus the cell
@@ -230,9 +219,9 @@ export const Default: Story = {
230
219
  const newOrgName = 'Salieri LLC';
231
220
  await userEvent.type(newSearchField, newOrgName);
232
221
 
233
- // Look for an option to select (should be the create new option)
234
- const newOption = await body.findAllByRole('option');
235
- await expect(newOption[0]).toBeVisible();
222
+ // Wait for the create option to appear (debounce is 200ms, allow time for render)
223
+ const createOption = await body.findByRole('option', undefined, { timeout: 500 });
224
+ await expect(createOption).toBeVisible();
236
225
 
237
226
  // Press Enter to select/create
238
227
  await userEvent.keyboard('{Enter}');