@dxos/react-ui-form 0.8.4-main.c1de068 → 0.8.4-main.c85a9c8dae

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