@dxos/react-ui-form 0.8.4-main.f9ba587 → 0.8.4-main.fcc0d83b33

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