@dxos/react-ui-form 0.8.4-main.e098934 → 0.8.4-main.ead640a

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 (88) hide show
  1. package/dist/lib/browser/index.mjs +422 -284
  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 +422 -284
  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.stories.d.ts +3 -2
  8. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +3 -2
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.d.ts +2 -2
  14. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form.stories.d.ts +12 -11
  16. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/FormContent.d.ts +5 -5
  18. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormContext.d.ts +2 -2
  20. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/Input.d.ts +1 -1
  22. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/RefField.d.ts +10 -6
  24. package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +3 -2
  26. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Form/factory.d.ts +1 -1
  28. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  29. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +41 -0
  30. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  31. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +82 -0
  32. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  33. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  34. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  35. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +19 -6
  36. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  37. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +7 -6
  38. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  39. package/dist/types/src/components/index.d.ts +1 -0
  40. package/dist/types/src/components/index.d.ts.map +1 -1
  41. package/dist/types/src/hooks/useForm.d.ts +2 -2
  42. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  43. package/dist/types/src/hooks/useQueryRefOptions.d.ts +9 -3
  44. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
  45. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
  46. package/dist/types/src/translations.d.ts +2 -1
  47. package/dist/types/src/translations.d.ts.map +1 -1
  48. package/dist/types/src/util/field.d.ts +1 -1
  49. package/dist/types/src/util/field.d.ts.map +1 -1
  50. package/dist/types/src/util/formatting.d.ts +1 -1
  51. package/dist/types/src/util/formatting.d.ts.map +1 -1
  52. package/dist/types/src/util/refs.d.ts +1 -1
  53. package/dist/types/src/util/refs.d.ts.map +1 -1
  54. package/dist/types/src/util/schema.d.ts +1 -1
  55. package/dist/types/src/util/schema.d.ts.map +1 -1
  56. package/dist/types/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +39 -39
  58. package/src/components/ControlSection/ControlSection.stories.tsx +3 -4
  59. package/src/components/ControlSection/ControlSection.tsx +1 -1
  60. package/src/components/FieldEditor/FieldEditor.stories.tsx +6 -5
  61. package/src/components/FieldEditor/FieldEditor.tsx +5 -2
  62. package/src/components/Form/ArrayField.tsx +4 -3
  63. package/src/components/Form/Form.stories.tsx +32 -32
  64. package/src/components/Form/Form.tsx +13 -5
  65. package/src/components/Form/FormContent.tsx +30 -10
  66. package/src/components/Form/FormContext.tsx +3 -3
  67. package/src/components/Form/Input.tsx +1 -1
  68. package/src/components/Form/RefField.tsx +48 -64
  69. package/src/components/Form/custom/GeoPointInput.tsx +1 -1
  70. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +5 -6
  71. package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
  72. package/src/components/Form/factory.tsx +1 -1
  73. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
  74. package/src/components/ObjectPicker/ObjectPicker.tsx +161 -0
  75. package/src/components/ObjectPicker/index.ts +5 -0
  76. package/src/components/ViewEditor/ViewEditor.stories.tsx +95 -53
  77. package/src/components/ViewEditor/ViewEditor.test.tsx +4 -3
  78. package/src/components/ViewEditor/ViewEditor.tsx +270 -229
  79. package/src/components/index.ts +1 -0
  80. package/src/components/testing.tsx +2 -2
  81. package/src/hooks/useForm.ts +7 -4
  82. package/src/hooks/useQueryRefOptions.ts +15 -6
  83. package/src/hooks/useRefQueryLookupHandler.ts +3 -3
  84. package/src/translations.ts +2 -1
  85. package/src/util/field.ts +1 -1
  86. package/src/util/formatting.ts +1 -1
  87. package/src/util/refs.ts +2 -2
  88. package/src/util/schema.ts +2 -4
@@ -2,26 +2,31 @@
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, useEffect, useMemo, useState } from 'react';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
8
 
11
- import { Filter, Obj, Query, Type } from '@dxos/echo';
12
- import { type EchoSchema, Format, toJsonSchema } from '@dxos/echo-schema';
13
- import { 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 { QueryParser, createFilter } from '@dxos/react-ui-components';
17
- import { type DataType, ProjectionModel, createView, typenameFromQuery } from '@dxos/schema';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
14
+ import { withTheme } from '@dxos/react-ui/testing';
15
+ import { DataType, type ProjectionModel, createView, getTypenameFromQuery } from '@dxos/schema';
19
16
 
20
17
  import { translations } from '../../translations';
21
18
  import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
22
19
 
23
20
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
24
21
 
22
+ const types = [
23
+ // TODO(burdon): Get label from annotation.
24
+ { value: Type.getTypename(DataType.Organization), label: 'Organization' },
25
+ { value: Type.getTypename(DataType.Person), label: 'Person' },
26
+ { value: Type.getTypename(DataType.Project), label: 'Project' },
27
+ { value: Type.getTypename(DataType.Employer), label: 'Employer' },
28
+ ];
29
+
25
30
  // Type definition for debug objects exposed to tests.
26
31
  export type ViewEditorDebugObjects = {
27
32
  schema: EchoSchema;
@@ -29,13 +34,16 @@ export type ViewEditorDebugObjects = {
29
34
  projection: ProjectionModel;
30
35
  };
31
36
 
32
- type StoryProps = Pick<ViewEditorProps, 'readonly' | 'kind'>;
37
+ type StoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
33
38
 
34
39
  const DefaultStory = (props: StoryProps) => {
35
- const space = useSpace();
40
+ const { space } = useClientProvider();
36
41
  const [schema, setSchema] = useState<EchoSchema>();
37
42
  const [view, setView] = useState<DataType.View>();
38
- const [projection, setProjection] = useState<ProjectionModel>();
43
+ const projectionRef = useRef<ProjectionModel>(null);
44
+
45
+ const tags = useQuery(space, Filter.type(Tag.Tag));
46
+
39
47
  useAsyncEffect(async () => {
40
48
  if (space) {
41
49
  const TestSchema = Schema.Struct({
@@ -49,65 +57,86 @@ const DefaultStory = (props: StoryProps) => {
49
57
  }),
50
58
  );
51
59
 
52
- const [schema] = await space.db.schemaRegistry.register([TestSchema]);
60
+ const AlternateSchema = Schema.Struct({
61
+ title: Schema.String,
62
+ description: Schema.String,
63
+ completed: Schema.Boolean,
64
+ }).pipe(
65
+ Type.Obj({
66
+ typename: 'example.com/type/Alternate',
67
+ version: '0.1.0',
68
+ }),
69
+ );
70
+
71
+ const [testSchema] = await space.db.schemaRegistry.register([TestSchema, AlternateSchema]);
53
72
  const view = createView({
54
73
  name: 'Test',
55
- query: Query.select(Filter.type(TestSchema)),
56
- jsonSchema: toJsonSchema(TestSchema),
74
+ query: Query.select(Filter.typename(TestSchema.typename)),
75
+ jsonSchema: Type.toJsonSchema(TestSchema),
57
76
  presentation: Obj.make(Type.Expando, {}),
58
77
  });
59
- const projection = new ProjectionModel(schema.jsonSchema, view.projection);
60
78
 
61
- setSchema(schema);
79
+ setSchema(testSchema);
62
80
  setView(view);
63
- setProjection(projection);
64
81
  }
65
82
  }, [space]);
66
83
 
67
84
  const updateViewQuery = useCallback(
68
- (newQueryString: string) => {
69
- if (!schema || !view) {
85
+ async (newQuery: QueryAST.Query, target?: string) => {
86
+ if (!schema || !view || !space) {
70
87
  return;
71
88
  }
72
89
 
73
- if (props.kind === 'advanced') {
74
- try {
75
- const parser = new QueryParser(newQueryString);
76
- // TODO(wittjosiah): When this fails it should show validation errors in the UI.
77
- const newQuery = Query.select(createFilter(parser.parse()));
78
- view.query = newQuery.ast;
79
-
80
- const typename = typenameFromQuery(newQuery.ast);
81
- if (typename) {
82
- schema.updateTypename(typename);
83
- }
84
- } catch {}
90
+ if (props.mode === 'query') {
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 = createView({
102
+ query,
103
+ jsonSchema: newSchema.jsonSchema,
104
+ presentation: Obj.make(Type.Expando, {}),
105
+ });
106
+ view.projection = Obj.getSnapshot(newView).projection;
107
+ setSchema(() => newSchema);
85
108
  } else {
86
- const typename = newQueryString;
87
- const newQuery = Query.select(Filter.typename(typename));
88
- view.query = newQuery.ast;
89
- schema.updateTypename(typename);
109
+ console.log('updateViewQuery', { newQuery });
110
+ view.query.ast = newQuery;
111
+ schema.updateTypename(getTypenameFromQuery(newQuery));
90
112
  }
91
113
  },
92
114
  [view, schema],
93
115
  );
94
116
 
95
- const handleDelete = useCallback((property: string) => projection?.deleteFieldProjection(property), [projection]);
117
+ const handleDelete = useCallback(
118
+ (property: string) => projectionRef.current?.deleteFieldProjection(property),
119
+ [projectionRef],
120
+ );
96
121
 
97
122
  // Expose objects on window for test access.
98
123
  useEffect(() => {
99
- if (typeof window !== 'undefined' && schema && view && projection) {
100
- (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = { schema, view, projection } satisfies ViewEditorDebugObjects;
124
+ if (typeof window !== 'undefined' && schema && view && projectionRef.current) {
125
+ (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = {
126
+ schema,
127
+ view,
128
+ projection: projectionRef.current,
129
+ } satisfies ViewEditorDebugObjects;
101
130
  }
102
- }, [schema, view, projection]);
131
+ }, [schema, view]);
103
132
 
104
133
  // NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
105
134
  const json = useMemo(
106
- () => JSON.parse(JSON.stringify({ schema, view, projection })),
107
- [JSON.stringify(schema), JSON.stringify(view), JSON.stringify(projection)],
135
+ () => JSON.parse(JSON.stringify({ schema, view, projection: projectionRef.current })),
136
+ [JSON.stringify(schema), JSON.stringify(view)],
108
137
  );
109
138
 
110
- if (!schema || !view || !projection) {
139
+ if (!schema || !view) {
111
140
  return <div />;
112
141
  }
113
142
 
@@ -115,11 +144,14 @@ const DefaultStory = (props: StoryProps) => {
115
144
  <TestLayout json={json}>
116
145
  <TestPanel>
117
146
  <ViewEditor
147
+ ref={projectionRef}
118
148
  schema={schema}
119
149
  view={view}
120
150
  registry={space?.db.schemaRegistry}
121
- kind={props.kind}
151
+ mode={props.mode}
122
152
  readonly={props.readonly}
153
+ types={types}
154
+ tags={tags}
123
155
  onQueryChanged={updateViewQuery}
124
156
  onDelete={handleDelete}
125
157
  />
@@ -134,13 +166,19 @@ const meta = {
134
166
  decorators: [
135
167
  withClientProvider({
136
168
  createSpace: true,
137
- }),
138
- withLayout({
139
- fullscreen: true,
169
+ types: [Tag.Tag],
170
+ onCreateSpace: ({ space }) => {
171
+ space.db.add(Tag.make({ label: 'Important' }));
172
+ space.db.add(Tag.make({ label: 'Investor' }));
173
+ space.db.add(Tag.make({ label: 'New' }));
174
+ },
140
175
  }),
141
176
  withTheme,
142
177
  ],
143
- parameters: { translations },
178
+ parameters: {
179
+ layout: 'fullscreen',
180
+ translations,
181
+ },
144
182
  } satisfies Meta<typeof DefaultStory>;
145
183
 
146
184
  export default meta;
@@ -150,9 +188,13 @@ type Story = StoryObj<typeof meta>;
150
188
  export const Default: Story = {};
151
189
 
152
190
  export const Readonly: Story = {
153
- args: { readonly: true },
191
+ args: {
192
+ readonly: true,
193
+ },
154
194
  };
155
195
 
156
- export const Advanced: Story = {
157
- args: { kind: 'advanced' },
196
+ export const QueryMode: Story = {
197
+ args: {
198
+ mode: 'query',
199
+ },
158
200
  };
@@ -6,7 +6,7 @@ import { composeStories } from '@storybook/react';
6
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';
9
+ import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
10
10
  import { DataType, ProjectionModel } from '@dxos/schema';
11
11
 
12
12
  import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
@@ -134,7 +134,8 @@ describe('ViewEditor', () => {
134
134
  expect(addedPropertyProjection!.props.property).toBe('added_property');
135
135
  });
136
136
 
137
- test('delete property', async () => {
137
+ // TODO(mykola): Fix flaky test.
138
+ test('delete property', { retry: 2 }, async () => {
138
139
  await Default.run();
139
140
  await waitForViewEditor();
140
141
 
@@ -200,4 +201,4 @@ describe('ViewEditor', () => {
200
201
  // Also verify that the field is back in the visible fields list.
201
202
  expect(debugObjects.view.projection.fields.filter(({ visible }) => visible !== false).length).toBeGreaterThan(0);
202
203
  });
203
- });
204
+ }, 30_000);