@dxos/react-ui-form 0.8.4-main.3a94e84 → 0.8.4-main.3c1ae3b

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