@dxos/react-ui-form 0.8.4-main.fd6878d → 0.8.4-main.fffef41
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.
- package/dist/lib/browser/index.mjs +806 -572
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +806 -572
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +2 -2
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +80 -5
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +1 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +75 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +2 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +6 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +128 -32
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +16 -10
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +2 -2
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +5 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +14 -4
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +75 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +41 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +81 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +20 -6
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +79 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +6 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +2 -2
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +9 -3
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +11 -7
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field.d.ts +1 -1
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/refs.d.ts +1 -1
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -36
- package/src/components/ControlSection/ControlSection.stories.tsx +10 -14
- package/src/components/ControlSection/ControlSection.tsx +26 -12
- package/src/components/FieldEditor/FieldEditor.stories.tsx +10 -11
- package/src/components/FieldEditor/FieldEditor.tsx +12 -14
- package/src/components/Form/ArrayField.tsx +6 -6
- package/src/components/Form/Defaults.tsx +21 -20
- package/src/components/Form/Form.stories.tsx +132 -97
- package/src/components/Form/Form.tsx +27 -14
- package/src/components/Form/FormActions.tsx +2 -2
- package/src/components/Form/FormContent.tsx +166 -45
- package/src/components/Form/FormContext.tsx +3 -3
- package/src/components/Form/Input.tsx +5 -2
- package/src/components/Form/RefField.tsx +107 -38
- package/src/components/Form/custom/GeoPointInput.tsx +28 -20
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +9 -10
- package/src/components/Form/custom/SelectOptionsInput.tsx +10 -10
- package/src/components/Form/custom/TupleInput.tsx +2 -2
- package/src/components/Form/factory.tsx +1 -1
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +161 -0
- package/src/components/ObjectPicker/index.ts +5 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +123 -49
- package/src/components/ViewEditor/ViewEditor.test.tsx +8 -11
- package/src/components/ViewEditor/ViewEditor.tsx +281 -233
- package/src/components/index.ts +1 -2
- package/src/components/testing.tsx +15 -9
- package/src/hooks/useForm.ts +7 -4
- package/src/hooks/useQueryRefOptions.ts +15 -6
- package/src/hooks/useRefQueryLookupHandler.ts +3 -3
- package/src/translations.ts +13 -8
- package/src/util/field.ts +9 -19
- package/src/util/formatting.ts +1 -1
- package/src/util/refs.ts +7 -7
- package/src/util/schema.ts +2 -4
- package/dist/types/src/components/deprecated/Form.d.ts +0 -15
- package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/index.d.ts +0 -2
- package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
- package/src/components/deprecated/Form.stories.tsx +0 -63
- package/src/components/deprecated/Form.tsx +0 -119
- package/src/components/deprecated/FormInput.tsx +0 -71
- package/src/components/deprecated/index.ts +0 -5
|
@@ -2,94 +2,142 @@
|
|
|
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
|
|
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 { Obj, Type } from '@dxos/echo';
|
|
12
|
-
import { type EchoSchema, Format
|
|
13
|
-
import {
|
|
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 {
|
|
17
|
-
import {
|
|
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
18
|
import { translations } from '../../translations';
|
|
20
19
|
import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
21
20
|
|
|
22
21
|
import { ViewEditor, type ViewEditorProps } from './ViewEditor';
|
|
23
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: Type.getTypename(Project.Project), label: 'Project' },
|
|
28
|
+
{ value: Employer.Employer.typename, label: 'Employer' },
|
|
29
|
+
];
|
|
30
|
+
|
|
24
31
|
// Type definition for debug objects exposed to tests.
|
|
25
32
|
export type ViewEditorDebugObjects = {
|
|
26
33
|
schema: EchoSchema;
|
|
27
|
-
view:
|
|
34
|
+
view: View.View;
|
|
28
35
|
projection: ProjectionModel;
|
|
29
36
|
};
|
|
30
37
|
|
|
31
|
-
type StoryProps = Pick<ViewEditorProps, 'readonly'>;
|
|
38
|
+
type StoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
|
|
32
39
|
|
|
33
40
|
const DefaultStory = (props: StoryProps) => {
|
|
34
|
-
const space =
|
|
41
|
+
const { space } = useClientProvider();
|
|
35
42
|
const [schema, setSchema] = useState<EchoSchema>();
|
|
36
|
-
const [view, setView] = useState<
|
|
37
|
-
const
|
|
43
|
+
const [view, setView] = useState<View.View>();
|
|
44
|
+
const projectionRef = useRef<ProjectionModel>(null);
|
|
45
|
+
|
|
46
|
+
const tags = useQuery(space, Filter.type(Tag.Tag));
|
|
47
|
+
|
|
38
48
|
useAsyncEffect(async () => {
|
|
39
49
|
if (space) {
|
|
40
|
-
|
|
50
|
+
const TestSchema = Schema.Struct({
|
|
41
51
|
name: Schema.String,
|
|
42
52
|
email: Format.Email,
|
|
43
53
|
salary: Format.Currency(),
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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({
|
|
48
74
|
name: 'Test',
|
|
49
|
-
|
|
50
|
-
jsonSchema: toJsonSchema(TestSchema),
|
|
75
|
+
query: Query.select(Filter.type(TestSchema)),
|
|
76
|
+
jsonSchema: Type.toJsonSchema(TestSchema),
|
|
51
77
|
presentation: Obj.make(Type.Expando, {}),
|
|
52
78
|
});
|
|
53
|
-
const projection = new ProjectionModel(schema.jsonSchema, view.projection);
|
|
54
79
|
|
|
55
|
-
setSchema(
|
|
80
|
+
setSchema(testSchema);
|
|
56
81
|
setView(view);
|
|
57
|
-
setProjection(projection);
|
|
58
82
|
}
|
|
59
83
|
}, [space]);
|
|
60
84
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
(newTypename: string) => {
|
|
65
|
-
if (!schema) {
|
|
85
|
+
const updateViewQuery = useCallback(
|
|
86
|
+
async (newQuery: QueryAST.Query, target?: string) => {
|
|
87
|
+
if (!schema || !view || !space) {
|
|
66
88
|
return;
|
|
67
89
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
90
|
+
|
|
91
|
+
if (props.mode === 'query') {
|
|
92
|
+
const queue = target && DXN.tryParse(target) ? target : undefined;
|
|
93
|
+
const query = queue ? Query.fromAst(newQuery).options({ queues: [queue] }) : Query.fromAst(newQuery);
|
|
94
|
+
view.query.ast = query.ast;
|
|
95
|
+
|
|
96
|
+
const typename = getTypenameFromQuery(query.ast);
|
|
97
|
+
const [newSchema] = await space.db.schemaRegistry.query({ typename }).run();
|
|
98
|
+
if (!newSchema) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const newView = View.make({
|
|
103
|
+
query,
|
|
104
|
+
jsonSchema: newSchema.jsonSchema,
|
|
105
|
+
presentation: Obj.make(Type.Expando, {}),
|
|
106
|
+
});
|
|
107
|
+
view.projection = Obj.getSnapshot(newView).projection;
|
|
108
|
+
setSchema(() => newSchema);
|
|
109
|
+
} else {
|
|
110
|
+
console.log('updateViewQuery', { newQuery });
|
|
111
|
+
view.query.ast = newQuery;
|
|
112
|
+
schema.updateTypename(getTypenameFromQuery(newQuery));
|
|
71
113
|
}
|
|
72
|
-
schema.updateTypename(newTypename);
|
|
73
114
|
},
|
|
74
|
-
[
|
|
115
|
+
[view, schema],
|
|
75
116
|
);
|
|
76
117
|
|
|
77
|
-
const handleDelete = useCallback(
|
|
118
|
+
const handleDelete = useCallback(
|
|
119
|
+
(property: string) => projectionRef.current?.deleteFieldProjection(property),
|
|
120
|
+
[projectionRef],
|
|
121
|
+
);
|
|
78
122
|
|
|
79
123
|
// Expose objects on window for test access.
|
|
80
124
|
useEffect(() => {
|
|
81
|
-
if (typeof window !== 'undefined' && schema && view &&
|
|
82
|
-
(window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = {
|
|
125
|
+
if (typeof window !== 'undefined' && schema && view && projectionRef.current) {
|
|
126
|
+
(window as any)[VIEW_EDITOR_DEBUG_SYMBOL] = {
|
|
127
|
+
schema,
|
|
128
|
+
view,
|
|
129
|
+
projection: projectionRef.current,
|
|
130
|
+
} satisfies ViewEditorDebugObjects;
|
|
83
131
|
}
|
|
84
|
-
}, [schema, view
|
|
132
|
+
}, [schema, view]);
|
|
85
133
|
|
|
86
134
|
// NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
|
|
87
135
|
const json = useMemo(
|
|
88
|
-
() => JSON.parse(JSON.stringify({ schema, view, projection })),
|
|
89
|
-
[JSON.stringify(schema), JSON.stringify(view)
|
|
136
|
+
() => JSON.parse(JSON.stringify({ schema, view, projection: projectionRef.current })),
|
|
137
|
+
[JSON.stringify(schema), JSON.stringify(view)],
|
|
90
138
|
);
|
|
91
139
|
|
|
92
|
-
if (!schema || !view
|
|
140
|
+
if (!schema || !view) {
|
|
93
141
|
return <div />;
|
|
94
142
|
}
|
|
95
143
|
|
|
@@ -97,11 +145,15 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
97
145
|
<TestLayout json={json}>
|
|
98
146
|
<TestPanel>
|
|
99
147
|
<ViewEditor
|
|
148
|
+
ref={projectionRef}
|
|
100
149
|
schema={schema}
|
|
101
150
|
view={view}
|
|
102
151
|
registry={space?.db.schemaRegistry}
|
|
152
|
+
mode={props.mode}
|
|
103
153
|
readonly={props.readonly}
|
|
104
|
-
|
|
154
|
+
types={types}
|
|
155
|
+
tags={tags}
|
|
156
|
+
onQueryChanged={updateViewQuery}
|
|
105
157
|
onDelete={handleDelete}
|
|
106
158
|
/>
|
|
107
159
|
</TestPanel>
|
|
@@ -109,19 +161,41 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
109
161
|
);
|
|
110
162
|
};
|
|
111
163
|
|
|
112
|
-
const meta
|
|
164
|
+
const meta = {
|
|
113
165
|
title: 'ui/react-ui-form/ViewEditor',
|
|
114
166
|
render: DefaultStory,
|
|
115
|
-
decorators: [
|
|
116
|
-
|
|
117
|
-
|
|
167
|
+
decorators: [
|
|
168
|
+
withClientProvider({
|
|
169
|
+
createSpace: true,
|
|
170
|
+
types: [Tag.Tag],
|
|
171
|
+
onCreateSpace: ({ space }) => {
|
|
172
|
+
space.db.add(Tag.make({ label: 'Important' }));
|
|
173
|
+
space.db.add(Tag.make({ label: 'Investor' }));
|
|
174
|
+
space.db.add(Tag.make({ label: 'New' }));
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
withTheme,
|
|
178
|
+
],
|
|
179
|
+
parameters: {
|
|
180
|
+
layout: 'fullscreen',
|
|
181
|
+
translations,
|
|
182
|
+
},
|
|
183
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
118
184
|
|
|
119
185
|
export default meta;
|
|
120
186
|
|
|
121
|
-
type Story = StoryObj<
|
|
187
|
+
type Story = StoryObj<typeof meta>;
|
|
122
188
|
|
|
123
189
|
export const Default: Story = {};
|
|
124
190
|
|
|
125
191
|
export const Readonly: Story = {
|
|
126
|
-
args: {
|
|
192
|
+
args: {
|
|
193
|
+
readonly: true,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const QueryMode: Story = {
|
|
198
|
+
args: {
|
|
199
|
+
mode: 'query',
|
|
200
|
+
},
|
|
127
201
|
};
|
|
@@ -6,8 +6,8 @@ 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
|
|
10
|
-
import {
|
|
9
|
+
import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
|
|
10
|
+
import { ProjectionModel, View } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
13
13
|
|
|
@@ -20,7 +20,7 @@ const getViewEditorDebugObjects = (): ViewEditorDebugObjects => {
|
|
|
20
20
|
const debugObjects = (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] as ViewEditorDebugObjects;
|
|
21
21
|
expect(debugObjects).toBeDefined();
|
|
22
22
|
expect(debugObjects.schema).toBeInstanceOf(EchoSchema);
|
|
23
|
-
expect(isInstanceOf(
|
|
23
|
+
expect(isInstanceOf(View.View, debugObjects.view)).toBeTruthy();
|
|
24
24
|
expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
|
|
25
25
|
return debugObjects;
|
|
26
26
|
};
|
|
@@ -134,7 +134,8 @@ describe('ViewEditor', () => {
|
|
|
134
134
|
expect(addedPropertyProjection!.props.property).toBe('added_property');
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
// TODO(mykola): Fix flaky test.
|
|
138
|
+
test('delete property', { retry: 2 }, async () => {
|
|
138
139
|
await Default.run();
|
|
139
140
|
await waitForViewEditor();
|
|
140
141
|
|
|
@@ -189,19 +190,15 @@ describe('ViewEditor', () => {
|
|
|
189
190
|
// Access the live objects directly from window using symbol.
|
|
190
191
|
const debugObjects = getViewEditorDebugObjects();
|
|
191
192
|
|
|
192
|
-
// Check that hiddenFields is non-empty.
|
|
193
|
-
expect(debugObjects.view.projection.hiddenFields).toBeDefined();
|
|
194
|
-
expect(debugObjects.view.projection.hiddenFields!.length).toBeGreaterThan(0);
|
|
195
|
-
|
|
196
193
|
// Click the show button
|
|
197
194
|
fireEvent.click(screen.getByTestId('show-field-button'));
|
|
198
195
|
|
|
199
196
|
// Wait for the data to update and check that hiddenFields is empty.
|
|
200
197
|
await waitFor(() => {
|
|
201
|
-
expect(debugObjects.view.projection.
|
|
198
|
+
expect(debugObjects.view.projection.fields.filter(({ visible }) => visible === false)!.length).toBe(0);
|
|
202
199
|
});
|
|
203
200
|
|
|
204
201
|
// Also verify that the field is back in the visible fields list.
|
|
205
|
-
expect(debugObjects.view.projection.fields.length).toBeGreaterThan(0);
|
|
202
|
+
expect(debugObjects.view.projection.fields.filter(({ visible }) => visible !== false).length).toBeGreaterThan(0);
|
|
206
203
|
});
|
|
207
|
-
});
|
|
204
|
+
}, 30_000);
|