@dxos/react-ui-form 0.8.4-main.b97322e → 0.8.4-main.bc2380dfbc

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 (247) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +0 -1
  3. package/dist/lib/browser/index.mjs +2768 -2135
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/translations.mjs +82 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +2768 -2135
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/translations.mjs +84 -0
  12. package/dist/lib/node-esm/translations.mjs.map +7 -0
  13. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +8 -7
  14. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  15. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +84 -5
  16. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Form.d.ts +109 -25
  18. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts +130 -56
  20. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField.d.ts +43 -0
  22. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts +79 -0
  24. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  25. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  26. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  27. package/dist/types/src/components/Form/FormFieldSet.d.ts +15 -0
  28. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  29. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  30. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  31. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  32. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  33. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  34. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  35. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +11 -0
  36. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  37. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +84 -0
  38. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
  39. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  40. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  41. package/dist/types/src/components/Form/fields/RefField.d.ts +22 -0
  42. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  43. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  44. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  45. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  46. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +84 -0
  48. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  49. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  50. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  51. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  52. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  53. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  54. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  55. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  56. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  57. package/dist/types/src/components/Form/index.d.ts +2 -4
  58. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  59. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
  60. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  61. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  62. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  63. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +68 -0
  64. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  65. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +87 -0
  66. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  67. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  68. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  69. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  70. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  71. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +127 -0
  72. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  73. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
  74. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
  75. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  76. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  77. package/dist/types/src/components/Settings/Settings.d.ts +51 -0
  78. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  79. package/dist/types/src/components/Settings/Settings.stories.d.ts +84 -0
  80. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  81. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
  82. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
  83. package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
  84. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
  85. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
  86. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
  87. package/dist/types/src/components/Settings/index.d.ts +4 -0
  88. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  89. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +21 -10
  90. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  91. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +86 -8
  92. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/index.d.ts +4 -2
  94. package/dist/types/src/components/index.d.ts.map +1 -1
  95. package/dist/types/src/components/testing.d.ts +5 -4
  96. package/dist/types/src/components/testing.d.ts.map +1 -1
  97. package/dist/types/src/hooks/index.d.ts +2 -3
  98. package/dist/types/src/hooks/index.d.ts.map +1 -1
  99. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  100. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  101. package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
  102. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  103. package/dist/types/src/index.d.ts +0 -2
  104. package/dist/types/src/index.d.ts.map +1 -1
  105. package/dist/types/src/translations.d.ts +65 -54
  106. package/dist/types/src/translations.d.ts.map +1 -1
  107. package/dist/types/src/util/field-type.d.ts +19 -0
  108. package/dist/types/src/util/field-type.d.ts.map +1 -0
  109. package/dist/types/src/util/field.d.ts +3 -3
  110. package/dist/types/src/util/field.d.ts.map +1 -1
  111. package/dist/types/src/util/formatting.d.ts +2 -2
  112. package/dist/types/src/util/formatting.d.ts.map +1 -1
  113. package/dist/types/src/util/index.d.ts +2 -1
  114. package/dist/types/src/util/index.d.ts.map +1 -1
  115. package/dist/types/src/util/properties.d.ts +7 -0
  116. package/dist/types/src/util/properties.d.ts.map +1 -0
  117. package/dist/types/src/util/refs.d.ts +3 -3
  118. package/dist/types/src/util/refs.d.ts.map +1 -1
  119. package/dist/types/tsconfig.tsbuildinfo +1 -1
  120. package/package.json +58 -47
  121. package/src/components/FieldEditor/FieldEditor.stories.tsx +51 -32
  122. package/src/components/FieldEditor/FieldEditor.test.tsx +2 -2
  123. package/src/components/FieldEditor/FieldEditor.tsx +89 -84
  124. package/src/components/Form/AUDIT.md +17 -0
  125. package/src/components/Form/Form.stories.tsx +147 -223
  126. package/src/components/Form/Form.tsx +375 -95
  127. package/src/components/Form/FormField.tsx +306 -0
  128. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  129. package/src/components/Form/FormFieldComponent.tsx +174 -0
  130. package/src/components/Form/FormFieldSet.tsx +144 -0
  131. package/src/components/Form/README.md +3 -0
  132. package/src/components/Form/fields/ArrayField.tsx +155 -0
  133. package/src/components/Form/fields/BooleanField.tsx +22 -0
  134. package/src/components/Form/fields/GeoPointField.tsx +121 -0
  135. package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
  136. package/src/components/Form/fields/MarkdownField.tsx +168 -0
  137. package/src/components/Form/fields/NumberField.tsx +39 -0
  138. package/src/components/Form/fields/RefField.tsx +229 -0
  139. package/src/components/Form/fields/SelectField.tsx +65 -0
  140. package/src/components/Form/fields/SelectOptionField.stories.tsx +66 -0
  141. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +51 -46
  142. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  143. package/src/components/Form/fields/TextField.tsx +38 -0
  144. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -10
  145. package/src/components/Form/fields/index.ts +15 -0
  146. package/src/components/Form/index.ts +3 -4
  147. package/src/components/ObjectForm/ObjectForm.tsx +114 -0
  148. package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
  149. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +212 -0
  150. package/src/components/ObjectPicker/ObjectPicker.tsx +202 -0
  151. package/src/components/ObjectPicker/index.ts +5 -0
  152. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +362 -0
  153. package/src/components/ObjectProperties/ObjectProperties.test.tsx +101 -0
  154. package/src/components/ObjectProperties/ObjectProperties.tsx +141 -0
  155. package/src/components/ObjectProperties/index.ts +5 -0
  156. package/src/components/Settings/AUDIT.md +52 -0
  157. package/src/components/Settings/Settings.stories.tsx +104 -0
  158. package/src/components/Settings/Settings.tsx +80 -0
  159. package/src/components/Settings/SettingsFieldSet.tsx +257 -0
  160. package/src/components/Settings/SettingsItem.tsx +58 -0
  161. package/src/components/Settings/SettingsSelectField.tsx +59 -0
  162. package/src/components/Settings/index.ts +7 -0
  163. package/src/components/ViewEditor/ViewEditor.stories.tsx +139 -62
  164. package/src/components/ViewEditor/ViewEditor.test.tsx +16 -18
  165. package/src/components/ViewEditor/ViewEditor.tsx +348 -205
  166. package/src/components/index.ts +4 -3
  167. package/src/components/testing.tsx +31 -12
  168. package/src/hooks/index.ts +2 -3
  169. package/src/hooks/useFormHandler.ts +380 -0
  170. package/src/hooks/useKeyHandler.tsx +37 -0
  171. package/src/index.ts +0 -4
  172. package/src/translations.ts +70 -58
  173. package/src/util/field-type.ts +60 -0
  174. package/src/util/field.ts +33 -43
  175. package/src/util/formatting.ts +22 -22
  176. package/src/util/index.ts +2 -1
  177. package/src/util/properties.ts +18 -0
  178. package/src/util/refs.ts +17 -19
  179. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  180. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  181. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
  182. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  183. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  184. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  185. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  186. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  187. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  188. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  189. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  190. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  191. package/dist/types/src/components/Form/FormContent.d.ts +0 -57
  192. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  193. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  194. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  195. package/dist/types/src/components/Form/Input.d.ts +0 -26
  196. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  197. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  198. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  199. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  200. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  201. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  202. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  203. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
  204. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  205. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  206. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  207. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  208. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  209. package/dist/types/src/components/Form/factory.d.ts +0 -8
  210. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  211. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  212. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  213. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  214. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  215. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  216. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  217. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  218. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  219. package/dist/types/src/hooks/useForm.d.ts +0 -67
  220. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  221. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
  222. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  223. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  224. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  225. package/dist/types/src/util/schema.d.ts +0 -3
  226. package/dist/types/src/util/schema.d.ts.map +0 -1
  227. package/src/components/ControlSection/ControlSection.stories.tsx +0 -41
  228. package/src/components/ControlSection/ControlSection.tsx +0 -139
  229. package/src/components/Form/ArrayField.tsx +0 -124
  230. package/src/components/Form/Defaults.tsx +0 -203
  231. package/src/components/Form/FormActions.tsx +0 -53
  232. package/src/components/Form/FormContent.tsx +0 -240
  233. package/src/components/Form/FormContext.tsx +0 -110
  234. package/src/components/Form/Input.tsx +0 -51
  235. package/src/components/Form/RefField.tsx +0 -213
  236. package/src/components/Form/custom/GeoPointInput.tsx +0 -72
  237. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +0 -61
  238. package/src/components/Form/custom/index.ts +0 -7
  239. package/src/components/Form/factory.tsx +0 -31
  240. package/src/components/deprecated/Form.stories.tsx +0 -62
  241. package/src/components/deprecated/Form.tsx +0 -119
  242. package/src/components/deprecated/FormInput.tsx +0 -71
  243. package/src/components/deprecated/index.ts +0 -5
  244. package/src/hooks/useForm.ts +0 -291
  245. package/src/hooks/useQueryRefOptions.ts +0 -53
  246. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  247. package/src/util/schema.ts +0 -14
@@ -2,125 +2,202 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import { Schema } from 'effect';
9
- import React, { useCallback, useMemo, useState, useEffect } from 'react';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
8
 
11
- import { Obj, Type } from '@dxos/echo';
12
- import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
13
- import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
14
- import { withClientProvider } from '@dxos/react-client/testing';
9
+ import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Tag, Type, type View } from '@dxos/echo';
10
+ import { type EchoSchema, Format, type Mutable } from '@dxos/echo/internal';
11
+ import { useQuery } from '@dxos/react-client/echo';
12
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
15
13
  import { useAsyncEffect } from '@dxos/react-ui';
16
- import { DataType, ProjectionModel, createView } from '@dxos/schema';
17
- import { withTheme, withLayout } from '@dxos/storybook-utils';
14
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
+ import { type ProjectionModel, ViewModel, getTypenameFromQuery } from '@dxos/schema';
16
+ import { Employer, Organization, Person, Pipeline } from '@dxos/types';
17
+
18
+ import { translations } from '#translations';
18
19
 
20
+ import { TestLayout, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
19
21
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
20
- import { translations } from '../../translations';
21
- import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
22
+
23
+ const types = [
24
+ // TODO(burdon): Get label from annotation.
25
+ { value: Organization.Organization.typename, label: 'Organization' },
26
+ { value: Person.Person.typename, label: 'Person' },
27
+ { value: Pipeline.Pipeline.typename, label: 'Project' },
28
+ { value: Employer.Employer.typename, label: 'Employer' },
29
+ ];
22
30
 
23
31
  // Type definition for debug objects exposed to tests.
24
32
  export type ViewEditorDebugObjects = {
25
33
  schema: EchoSchema;
26
- view: DataType.View;
34
+ view: View.View;
27
35
  projection: ProjectionModel;
28
36
  };
29
37
 
30
- type StoryProps = Pick<ViewEditorProps, 'readonly'>;
38
+ type DefaultStoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
31
39
 
32
- const DefaultStory = (props: StoryProps) => {
33
- const space = useSpace();
40
+ const DefaultStory = (props: DefaultStoryProps) => {
41
+ const { space } = useClientStory();
34
42
  const [schema, setSchema] = useState<EchoSchema>();
35
- const [view, setView] = useState<DataType.View>();
36
- const [projection, setProjection] = useState<ProjectionModel>();
43
+ const [view, setView] = useState<View.View>();
44
+ const projectionRef = useRef<ProjectionModel>(null);
45
+
46
+ const tags = useQuery(space?.db, Filter.type(Tag.Tag));
47
+
37
48
  useAsyncEffect(async () => {
38
49
  if (space) {
39
- class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
50
+ const TestSchema = Schema.Struct({
40
51
  name: Schema.String,
41
52
  email: Format.Email,
42
53
  salary: Format.Currency(),
43
- }) {}
44
-
45
- const [schema] = await space.db.schemaRegistry.register([TestSchema]);
46
- const view = createView({
54
+ }).pipe(
55
+ Type.object({
56
+ typename: 'com.example.type.test',
57
+ version: '0.1.0',
58
+ }),
59
+ );
60
+
61
+ const AlternateSchema = Schema.Struct({
62
+ title: Schema.String,
63
+ description: Schema.String,
64
+ completed: Schema.Boolean,
65
+ }).pipe(
66
+ Type.object({
67
+ typename: 'com.example.type.alternate',
68
+ version: '0.1.0',
69
+ }),
70
+ );
71
+
72
+ const [testSchema] = await space.db.schemaRegistry.register([TestSchema, AlternateSchema]);
73
+ const view = ViewModel.make({
47
74
  name: 'Test',
48
- typename: schema.typename,
49
- jsonSchema: toJsonSchema(TestSchema),
50
- presentation: Obj.make(Type.Expando, {}),
75
+ query: Query.select(Filter.type(TestSchema)),
76
+ jsonSchema: JsonSchema.toJsonSchema(TestSchema),
51
77
  });
52
- const projection = new ProjectionModel(schema.jsonSchema, view.projection);
53
78
 
54
- setSchema(schema);
79
+ setSchema(testSchema);
55
80
  setView(view);
56
- setProjection(projection);
57
81
  }
58
82
  }, [space]);
59
83
 
60
- const views = useQuery(space, Filter.type(DataType.View));
61
- const currentTypename = useMemo(() => view?.query?.typename, [view]);
62
- const updateViewTypename = useCallback(
63
- (newTypename: string) => {
64
- if (!schema) {
84
+ const updateViewQuery = useCallback(
85
+ async (newQuery: QueryAST.Query, target?: string) => {
86
+ if (!schema || !view || !space) {
65
87
  return;
66
88
  }
67
- const matchingViews = views.filter((view) => view.query.typename === currentTypename);
68
- for (const view of matchingViews) {
69
- view.query.typename = newTypename;
89
+
90
+ if (props.mode === 'tag') {
91
+ const queue = target && DXN.tryParse(target) ? target : undefined;
92
+ const query = queue ? Query.fromAst(newQuery).from({ feeds: [queue] }) : Query.fromAst(newQuery);
93
+ Obj.update(view, (view) => {
94
+ view.query.ast = query.ast as Mutable<typeof query.ast>;
95
+ });
96
+
97
+ const typename = getTypenameFromQuery(query.ast);
98
+ const [newSchema] = await space.db.schemaRegistry.query({ typename }).run();
99
+ if (!newSchema) {
100
+ return;
101
+ }
102
+
103
+ const newView = ViewModel.make({
104
+ query,
105
+ jsonSchema: newSchema.jsonSchema,
106
+ });
107
+ Obj.update(view, (view) => {
108
+ view.projection = Obj.getSnapshot(newView).projection as Mutable<typeof view.projection>;
109
+ });
110
+ setSchema(() => newSchema);
111
+ } else {
112
+ Obj.update(view, (view) => {
113
+ view.query.ast = newQuery as Mutable<typeof newQuery>;
114
+ });
115
+ schema.updateTypename(getTypenameFromQuery(newQuery));
70
116
  }
71
- schema.updateTypename(newTypename);
72
117
  },
73
- [views, schema],
118
+ [view, schema],
74
119
  );
75
120
 
76
- const handleDelete = useCallback((property: string) => projection?.deleteFieldProjection(property), [projection]);
121
+ const handleDelete = useCallback(
122
+ (property: string) => projectionRef.current?.deleteFieldProjection(property),
123
+ [projectionRef],
124
+ );
77
125
 
78
126
  // Expose objects on window for test access.
79
127
  useEffect(() => {
80
- if (typeof window !== 'undefined' && schema && view && projection) {
81
- (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = { schema, view, projection } satisfies ViewEditorDebugObjects;
128
+ if (typeof window !== 'undefined' && schema && view && projectionRef.current) {
129
+ (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = {
130
+ schema,
131
+ view,
132
+ projection: projectionRef.current,
133
+ } satisfies ViewEditorDebugObjects;
82
134
  }
83
- }, [schema, view, projection]);
135
+ }, [schema, view]);
84
136
 
85
137
  // NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
86
138
  const json = useMemo(
87
- () => JSON.parse(JSON.stringify({ schema, view, projection })),
88
- [JSON.stringify(schema), JSON.stringify(view), JSON.stringify(projection)],
139
+ () => JSON.parse(JSON.stringify({ schema, view, projection: projectionRef.current })),
140
+ [JSON.stringify(schema), JSON.stringify(view)],
89
141
  );
90
142
 
91
- if (!schema || !view || !projection) {
143
+ if (!schema || !view) {
92
144
  return <div />;
93
145
  }
94
146
 
95
147
  return (
96
148
  <TestLayout json={json}>
97
- <TestPanel>
98
- <ViewEditor
99
- schema={schema}
100
- view={view}
101
- registry={space?.db.schemaRegistry}
102
- readonly={props.readonly}
103
- onTypenameChanged={updateViewTypename}
104
- onDelete={handleDelete}
105
- />
106
- </TestPanel>
149
+ <ViewEditor
150
+ ref={projectionRef}
151
+ schema={schema}
152
+ view={view}
153
+ registry={space?.db.schemaRegistry}
154
+ mode={props.mode}
155
+ readonly={props.readonly}
156
+ types={types}
157
+ tags={tags}
158
+ onQueryChanged={updateViewQuery}
159
+ onDelete={handleDelete}
160
+ />
107
161
  </TestLayout>
108
162
  );
109
163
  };
110
164
 
111
- const meta: Meta<StoryProps> = {
165
+ const meta = {
112
166
  title: 'ui/react-ui-form/ViewEditor',
113
167
  render: DefaultStory,
114
- decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true }), withTheme],
115
- parameters: { translations },
116
- };
168
+ decorators: [
169
+ withClientProvider({
170
+ createSpace: true,
171
+ types: [Tag.Tag],
172
+ onCreateSpace: ({ space }) => {
173
+ space.db.add(Tag.make({ label: 'Important' }));
174
+ space.db.add(Tag.make({ label: 'Investor' }));
175
+ space.db.add(Tag.make({ label: 'New' }));
176
+ },
177
+ }),
178
+ withTheme(),
179
+ withLayout({ layout: 'fullscreen' }),
180
+ ],
181
+ parameters: {
182
+ layout: 'fullscreen',
183
+ translations,
184
+ },
185
+ } satisfies Meta<typeof DefaultStory>;
117
186
 
118
187
  export default meta;
119
188
 
120
- type Story = StoryObj<StoryProps>;
189
+ type Story = StoryObj<typeof meta>;
121
190
 
122
191
  export const Default: Story = {};
123
192
 
124
193
  export const Readonly: Story = {
125
- args: { readonly: true },
194
+ args: {
195
+ readonly: true,
196
+ },
197
+ };
198
+
199
+ export const TagQueryMode: Story = {
200
+ args: {
201
+ mode: 'tag',
202
+ },
126
203
  };
@@ -3,15 +3,16 @@
3
3
  //
4
4
 
5
5
  import { composeStories } from '@storybook/react';
6
- import { screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
6
+ import { cleanup, fireEvent, screen, waitFor } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
- import { EchoSchema, isInstanceOf } from '@dxos/echo-schema';
10
- import { DataType, ProjectionModel } from '@dxos/schema';
9
+ import { View } from '@dxos/echo';
10
+ import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
11
+ import { ProjectionModel } from '@dxos/schema';
11
12
 
13
+ import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
12
14
  import * as stories from './ViewEditor.stories';
13
15
  import { type ViewEditorDebugObjects } from './ViewEditor.stories';
14
- import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
15
16
 
16
17
  const { Default } = composeStories(stories);
17
18
 
@@ -19,7 +20,7 @@ const getViewEditorDebugObjects = (): ViewEditorDebugObjects => {
19
20
  const debugObjects = (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] as ViewEditorDebugObjects;
20
21
  expect(debugObjects).toBeDefined();
21
22
  expect(debugObjects.schema).toBeInstanceOf(EchoSchema);
22
- expect(isInstanceOf(DataType.View, debugObjects.view)).toBeTruthy();
23
+ expect(isInstanceOf(View.View, debugObjects.view)).toBeTruthy();
23
24
  expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
24
25
  return debugObjects;
25
26
  };
@@ -41,7 +42,7 @@ describe('ViewEditor', () => {
41
42
  test('renders view editor', async () => {
42
43
  await Default.run();
43
44
  await waitForViewEditor();
44
- expect(screen.getByText('Typename')).toBeInTheDocument();
45
+ expect(screen.getByText('Record type')).toBeInTheDocument();
45
46
  });
46
47
 
47
48
  test('change property name', async () => {
@@ -53,7 +54,7 @@ describe('ViewEditor', () => {
53
54
  const nameField = screen.getByText('name');
54
55
  fireEvent.click(nameField);
55
56
 
56
- const fieldInput = screen.getByPlaceholderText('Field name.');
57
+ const fieldInput = screen.getByPlaceholderText('Property name');
57
58
  fireEvent.change(fieldInput, { target: { value: 'new_property' } });
58
59
 
59
60
  fireEvent.click(screen.getByTestId('save-button'));
@@ -88,12 +89,12 @@ describe('ViewEditor', () => {
88
89
  await Default.run();
89
90
  await waitForViewEditor();
90
91
 
91
- // Click the add property button.
92
+ // Click the add property button. The new field is auto-expanded by handleAdd.
92
93
  const addButton = screen.getByText('Add property');
93
94
  fireEvent.click(addButton);
94
95
 
95
96
  // Fill out the property field.
96
- const fieldInput = screen.getByPlaceholderText('Field name.');
97
+ const fieldInput = screen.getByPlaceholderText('Property name');
97
98
  fireEvent.change(fieldInput, { target: { value: 'added_property' } });
98
99
 
99
100
  // Click the format combo box and select the first option.
@@ -133,14 +134,15 @@ describe('ViewEditor', () => {
133
134
  expect(addedPropertyProjection!.props.property).toBe('added_property');
134
135
  });
135
136
 
136
- test('delete property', async () => {
137
+ // TODO(mykola): Fix flaky test.
138
+ test('delete property', { retry: 2 }, async () => {
137
139
  await Default.run();
138
140
  await waitForViewEditor();
139
141
 
140
142
  // Find the delete button for the 'name' property.
141
143
  const nameProperty = screen.getByText('name');
142
144
  const propertyRow = nameProperty.closest('[role="listitem"]');
143
- const deleteButton = propertyRow?.querySelector('button:last-child');
145
+ const deleteButton = propertyRow?.querySelector('[data-testid="field.delete"]');
144
146
 
145
147
  if (!deleteButton) {
146
148
  throw new Error('Delete button not found');
@@ -188,19 +190,15 @@ describe('ViewEditor', () => {
188
190
  // Access the live objects directly from window using symbol.
189
191
  const debugObjects = getViewEditorDebugObjects();
190
192
 
191
- // Check that hiddenFields is non-empty.
192
- expect(debugObjects.view.projection.hiddenFields).toBeDefined();
193
- expect(debugObjects.view.projection.hiddenFields!.length).toBeGreaterThan(0);
194
-
195
193
  // Click the show button
196
194
  fireEvent.click(screen.getByTestId('show-field-button'));
197
195
 
198
196
  // Wait for the data to update and check that hiddenFields is empty.
199
197
  await waitFor(() => {
200
- expect(debugObjects.view.projection.hiddenFields!.length).toBe(0);
198
+ expect(debugObjects.view.projection.fields.filter(({ visible }) => visible === false)!.length).toBe(0);
201
199
  });
202
200
 
203
201
  // Also verify that the field is back in the visible fields list.
204
- expect(debugObjects.view.projection.fields.length).toBeGreaterThan(0);
202
+ expect(debugObjects.view.projection.fields.filter(({ visible }) => visible !== false).length).toBeGreaterThan(0);
205
203
  });
206
- });
204
+ }, 30_000);