@dxos/react-ui-form 0.8.3 → 0.8.4-main.3a94e84
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 +69 -75
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +69 -75
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -4
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +3 -3
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +4 -4
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.d.ts +3 -3
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +56 -57
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -32
- package/src/components/ControlSection/ControlSection.stories.tsx +2 -2
- package/src/components/FieldEditor/FieldEditor.stories.tsx +14 -12
- package/src/components/FieldEditor/FieldEditor.test.tsx +11 -11
- package/src/components/FieldEditor/FieldEditor.tsx +5 -15
- package/src/components/Form/ArrayField.tsx +1 -1
- package/src/components/Form/Form.stories.tsx +2 -2
- package/src/components/Form/RefField.tsx +1 -1
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +2 -2
- package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +16 -10
- package/src/components/ViewEditor/ViewEditor.test.tsx +37 -35
- package/src/components/ViewEditor/ViewEditor.tsx +17 -32
- package/src/components/deprecated/Form.stories.tsx +5 -5
- package/src/components/deprecated/Form.tsx +4 -4
- package/src/components/testing.tsx +1 -1
- package/src/index.ts +1 -1
- package/src/translations.ts +4 -2
- package/dist/lib/node/index.cjs +0 -2416
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts +0 -10
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +0 -1
- package/src/components/ViewEditor/Test.stories.tsx +0 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-form",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.3a94e84",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -29,50 +29,48 @@
|
|
|
29
29
|
"@radix-ui/react-context": "1.1.1",
|
|
30
30
|
"date-fns": "^3.3.1",
|
|
31
31
|
"jsonpath": "^1.1.1",
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/echo-
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/keys": "0.8.
|
|
40
|
-
"@dxos/lit-ui": "0.8.
|
|
41
|
-
"@dxos/live-object": "0.8.
|
|
42
|
-
"@dxos/log": "0.8.
|
|
43
|
-
"@dxos/react-client": "0.8.
|
|
44
|
-
"@dxos/react-ui-list": "0.8.
|
|
45
|
-
"@dxos/react-ui-pickers": "0.8.
|
|
46
|
-
"@dxos/react-ui-stack": "0.8.
|
|
47
|
-
"@dxos/react-ui-tag-picker": "0.8.
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
32
|
+
"@dxos/async": "0.8.4-main.3a94e84",
|
|
33
|
+
"@dxos/debug": "0.8.4-main.3a94e84",
|
|
34
|
+
"@dxos/echo": "0.8.4-main.3a94e84",
|
|
35
|
+
"@dxos/echo-schema": "0.8.4-main.3a94e84",
|
|
36
|
+
"@dxos/effect": "0.8.4-main.3a94e84",
|
|
37
|
+
"@dxos/echo-db": "0.8.4-main.3a94e84",
|
|
38
|
+
"@dxos/invariant": "0.8.4-main.3a94e84",
|
|
39
|
+
"@dxos/keys": "0.8.4-main.3a94e84",
|
|
40
|
+
"@dxos/lit-ui": "0.8.4-main.3a94e84",
|
|
41
|
+
"@dxos/live-object": "0.8.4-main.3a94e84",
|
|
42
|
+
"@dxos/log": "0.8.4-main.3a94e84",
|
|
43
|
+
"@dxos/react-client": "0.8.4-main.3a94e84",
|
|
44
|
+
"@dxos/react-ui-list": "0.8.4-main.3a94e84",
|
|
45
|
+
"@dxos/react-ui-pickers": "0.8.4-main.3a94e84",
|
|
46
|
+
"@dxos/react-ui-stack": "0.8.4-main.3a94e84",
|
|
47
|
+
"@dxos/react-ui-tag-picker": "0.8.4-main.3a94e84",
|
|
48
|
+
"@dxos/schema": "0.8.4-main.3a94e84",
|
|
49
|
+
"@dxos/react-ui-types": "0.8.4-main.3a94e84",
|
|
50
|
+
"@dxos/util": "0.8.4-main.3a94e84"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
54
53
|
"@types/jsonpath": "^0.2.4",
|
|
55
54
|
"@types/react": "~18.2.0",
|
|
56
55
|
"@types/react-dom": "~18.2.0",
|
|
57
|
-
"effect": "3.
|
|
56
|
+
"effect": "3.17.0",
|
|
58
57
|
"fast-check": "^3.19.0",
|
|
59
58
|
"react": "~18.2.0",
|
|
60
59
|
"react-dom": "~18.2.0",
|
|
61
60
|
"vite": "5.4.7",
|
|
62
|
-
"@dxos/echo-signals": "0.8.
|
|
63
|
-
"@dxos/random": "0.8.
|
|
64
|
-
"@dxos/react-ui": "0.8.
|
|
65
|
-
"@dxos/react-ui-syntax-highlighter": "0.8.
|
|
66
|
-
"@dxos/react-ui-theme": "0.8.
|
|
67
|
-
"@dxos/storybook-utils": "0.8.
|
|
61
|
+
"@dxos/echo-signals": "0.8.4-main.3a94e84",
|
|
62
|
+
"@dxos/random": "0.8.4-main.3a94e84",
|
|
63
|
+
"@dxos/react-ui": "0.8.4-main.3a94e84",
|
|
64
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.3a94e84",
|
|
65
|
+
"@dxos/react-ui-theme": "0.8.4-main.3a94e84",
|
|
66
|
+
"@dxos/storybook-utils": "0.8.4-main.3a94e84"
|
|
68
67
|
},
|
|
69
68
|
"peerDependencies": {
|
|
70
|
-
"
|
|
71
|
-
"effect": "3.13.3",
|
|
69
|
+
"effect": "^3.13.3",
|
|
72
70
|
"react": "~18.2.0",
|
|
73
71
|
"react-dom": "~18.2.0",
|
|
74
|
-
"@dxos/react-ui": "0.8.
|
|
75
|
-
"@dxos/react-ui-theme": "0.8.
|
|
72
|
+
"@dxos/react-ui": "0.8.4-main.3a94e84",
|
|
73
|
+
"@dxos/react-ui-theme": "0.8.4-main.3a94e84"
|
|
76
74
|
},
|
|
77
75
|
"publishConfig": {
|
|
78
76
|
"access": "public"
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
|
|
10
10
|
import { StackItem } from '@dxos/react-ui-stack';
|
|
11
11
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
12
12
|
|
|
13
13
|
import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
|
|
14
|
-
import translations from '../../translations';
|
|
14
|
+
import { translations } from '../../translations';
|
|
15
15
|
|
|
16
16
|
const meta: Meta<ControlSectionProps> = {
|
|
17
17
|
title: 'ui/react-ui-form/ControlSection',
|
|
@@ -4,29 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import React, { useMemo, useEffect } from 'react';
|
|
9
9
|
|
|
10
10
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
|
-
import {
|
|
12
|
+
import { ProjectionModel } from '@dxos/schema';
|
|
13
13
|
import { TestSchema, testView } from '@dxos/schema/testing';
|
|
14
14
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
15
15
|
|
|
16
16
|
import { FieldEditor, type FieldEditorProps } from './FieldEditor';
|
|
17
|
-
import translations from '../../translations';
|
|
17
|
+
import { translations } from '../../translations';
|
|
18
18
|
import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
19
19
|
|
|
20
20
|
// Type definition for debug objects exposed to tests.
|
|
21
21
|
export type FieldEditorDebugObjects = {
|
|
22
22
|
props: FieldEditorProps;
|
|
23
|
-
projection:
|
|
23
|
+
projection: ProjectionModel;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
type StoryProps = FieldEditorProps;
|
|
27
27
|
|
|
28
28
|
const DefaultStory = (props: FieldEditorProps) => {
|
|
29
|
-
const projection = useMemo(() => new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView), []);
|
|
30
29
|
const handleComplete: FieldEditorProps['onSave'] = () => {
|
|
31
30
|
log.info('onClose', { props });
|
|
32
31
|
};
|
|
@@ -34,20 +33,23 @@ const DefaultStory = (props: FieldEditorProps) => {
|
|
|
34
33
|
// Expose objects on window for test access.
|
|
35
34
|
useEffect(() => {
|
|
36
35
|
if (typeof window !== 'undefined') {
|
|
37
|
-
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
|
|
36
|
+
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
|
|
37
|
+
props,
|
|
38
|
+
projection: props.projection,
|
|
39
|
+
} satisfies FieldEditorDebugObjects;
|
|
38
40
|
}
|
|
39
|
-
}, [props, projection]);
|
|
41
|
+
}, [props, props.projection]);
|
|
40
42
|
|
|
41
43
|
// NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
|
|
42
44
|
const json = useMemo(
|
|
43
|
-
() => JSON.parse(JSON.stringify({ props, projection })),
|
|
44
|
-
[JSON.stringify(props), JSON.stringify(projection)],
|
|
45
|
+
() => JSON.parse(JSON.stringify({ props, projection: props.projection })),
|
|
46
|
+
[JSON.stringify(props), JSON.stringify(props.projection)],
|
|
45
47
|
);
|
|
46
48
|
|
|
47
49
|
return (
|
|
48
50
|
<TestLayout json={json}>
|
|
49
51
|
<TestPanel>
|
|
50
|
-
<FieldEditor {...props}
|
|
52
|
+
<FieldEditor {...props} onSave={handleComplete} />
|
|
51
53
|
</TestPanel>
|
|
52
54
|
</TestLayout>
|
|
53
55
|
);
|
|
@@ -69,8 +71,8 @@ type Story = StoryObj<StoryProps>;
|
|
|
69
71
|
|
|
70
72
|
export const Default: Story = {
|
|
71
73
|
args: {
|
|
72
|
-
|
|
73
|
-
field: testView.fields[0],
|
|
74
|
+
projection: new ProjectionModel(createEchoSchema(TestSchema).jsonSchema, testView.projection),
|
|
75
|
+
field: testView.projection.fields[0],
|
|
74
76
|
},
|
|
75
77
|
parameters: { controls: { disabled: true } },
|
|
76
78
|
};
|
|
@@ -6,7 +6,7 @@ import { composeStories } from '@storybook/react';
|
|
|
6
6
|
import { screen, cleanup, fireEvent, act } from '@testing-library/react';
|
|
7
7
|
import { afterEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { ProjectionModel } from '@dxos/schema';
|
|
10
10
|
|
|
11
11
|
import * as stories from './FieldEditor.stories';
|
|
12
12
|
import { type FieldEditorDebugObjects } from './FieldEditor.stories';
|
|
@@ -14,14 +14,6 @@ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
|
14
14
|
|
|
15
15
|
const { Default } = composeStories(stories);
|
|
16
16
|
|
|
17
|
-
const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
|
|
18
|
-
const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
|
|
19
|
-
expect(debugObjects).toBeDefined();
|
|
20
|
-
expect(debugObjects.props).toBeInstanceOf(Object); // Props object
|
|
21
|
-
expect(debugObjects.projection).toBeInstanceOf(ViewProjection);
|
|
22
|
-
return debugObjects;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
17
|
describe('FieldEditor', () => {
|
|
26
18
|
afterEach(() => {
|
|
27
19
|
cleanup();
|
|
@@ -33,7 +25,7 @@ describe('FieldEditor', () => {
|
|
|
33
25
|
expect(screen.getByText('Type format')).toBeInTheDocument();
|
|
34
26
|
expect(screen.getByText('String')).toBeInTheDocument();
|
|
35
27
|
|
|
36
|
-
// Verify the initial field shows as String type
|
|
28
|
+
// Verify the initial field shows as String type.
|
|
37
29
|
expect(screen.getByText('String')).toBeInTheDocument();
|
|
38
30
|
|
|
39
31
|
fireEvent.click(screen.getByRole('combobox'));
|
|
@@ -49,9 +41,17 @@ describe('FieldEditor', () => {
|
|
|
49
41
|
// Access the live objects directly from window using symbol.
|
|
50
42
|
const debugObjects = getFieldEditorDebugObjects();
|
|
51
43
|
|
|
52
|
-
const name = debugObjects.projection.
|
|
44
|
+
const name = debugObjects.projection.baseSchema.properties!.name;
|
|
53
45
|
expect(name.type).toBe('number');
|
|
54
46
|
expect(name.format).toBe('number');
|
|
55
47
|
expect(name.description).toBe('Full name.');
|
|
56
48
|
});
|
|
57
49
|
});
|
|
50
|
+
|
|
51
|
+
const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
|
|
52
|
+
const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
|
|
53
|
+
expect(debugObjects).toBeDefined();
|
|
54
|
+
expect(debugObjects.props).toBeInstanceOf(Object); // Props object
|
|
55
|
+
expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
|
|
56
|
+
return debugObjects;
|
|
57
|
+
};
|
|
@@ -14,8 +14,7 @@ import {
|
|
|
14
14
|
sortProperties,
|
|
15
15
|
type FieldType,
|
|
16
16
|
type PropertyType,
|
|
17
|
-
type
|
|
18
|
-
type ViewProjection,
|
|
17
|
+
type ProjectionModel,
|
|
19
18
|
type SchemaProperty,
|
|
20
19
|
} from '@dxos/schema';
|
|
21
20
|
|
|
@@ -23,8 +22,7 @@ import { translationKey } from '../../translations';
|
|
|
23
22
|
import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
|
|
24
23
|
|
|
25
24
|
export type FieldEditorProps = {
|
|
26
|
-
|
|
27
|
-
projection: ViewProjection;
|
|
25
|
+
projection: ProjectionModel;
|
|
28
26
|
field: FieldType;
|
|
29
27
|
registry?: SchemaRegistry;
|
|
30
28
|
onSave: () => void;
|
|
@@ -34,15 +32,7 @@ export type FieldEditorProps = {
|
|
|
34
32
|
/**
|
|
35
33
|
* Displays a Form representing the metadata for a given `Field` and `View`.
|
|
36
34
|
*/
|
|
37
|
-
export const FieldEditor = ({
|
|
38
|
-
view,
|
|
39
|
-
projection,
|
|
40
|
-
field,
|
|
41
|
-
registry,
|
|
42
|
-
onSave,
|
|
43
|
-
onCancel,
|
|
44
|
-
outerSpacing,
|
|
45
|
-
}: FieldEditorProps) => {
|
|
35
|
+
export const FieldEditor = ({ projection, field, registry, onSave, onCancel, outerSpacing }: FieldEditorProps) => {
|
|
46
36
|
const { t } = useTranslation(translationKey);
|
|
47
37
|
const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
|
|
48
38
|
useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
|
|
@@ -112,7 +102,7 @@ export const FieldEditor = ({
|
|
|
112
102
|
|
|
113
103
|
const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
|
|
114
104
|
({ property }) => {
|
|
115
|
-
if (property &&
|
|
105
|
+
if (property && projection.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
116
106
|
return [
|
|
117
107
|
{
|
|
118
108
|
path: 'property',
|
|
@@ -121,7 +111,7 @@ export const FieldEditor = ({
|
|
|
121
111
|
];
|
|
122
112
|
}
|
|
123
113
|
},
|
|
124
|
-
[
|
|
114
|
+
[projection.fields, field],
|
|
125
115
|
);
|
|
126
116
|
|
|
127
117
|
const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
|
|
@@ -113,7 +113,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
113
113
|
</div>
|
|
114
114
|
{!readonly && (
|
|
115
115
|
<IconButton
|
|
116
|
-
classNames='is-full mlb-
|
|
116
|
+
classNames='is-full mlb-cardSpacingBlock flex'
|
|
117
117
|
icon='ph--plus--regular'
|
|
118
118
|
label={t('add field')}
|
|
119
119
|
onClick={handleAdd}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
9
|
import React, { useCallback, useState } from 'react';
|
|
10
10
|
|
|
@@ -17,7 +17,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
17
17
|
|
|
18
18
|
import { SelectInput } from './Defaults';
|
|
19
19
|
import { Form, type FormProps } from './Form';
|
|
20
|
-
import translations from '../../translations';
|
|
20
|
+
import { translations } from '../../translations';
|
|
21
21
|
import { TestLayout, TestPanel } from '../testing';
|
|
22
22
|
|
|
23
23
|
const AddressSchema = Schema.Struct({
|
|
@@ -156,7 +156,7 @@ export const RefField = ({
|
|
|
156
156
|
onBlur={(event) => onBlur(event as unknown as FocusEvent<HTMLElement>)}
|
|
157
157
|
onUpdate={handleUpdate}
|
|
158
158
|
onSearch={handleSearch}
|
|
159
|
-
classNames='
|
|
159
|
+
classNames='bg-inputSurface p-1.5 rounded-sm'
|
|
160
160
|
/>
|
|
161
161
|
)}
|
|
162
162
|
</div>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
9
|
import React, { useState } from 'react';
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
|
|
|
12
12
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
13
|
|
|
14
14
|
import { SelectOptionInput } from './SelectOptionsInput';
|
|
15
|
-
import translations from '../../../translations';
|
|
15
|
+
import { translations } from '../../../translations';
|
|
16
16
|
import { TestLayout, TestPanel } from '../../testing';
|
|
17
17
|
import { Form } from '../Form';
|
|
18
18
|
|
|
@@ -123,7 +123,7 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
|
|
|
123
123
|
<List.Item
|
|
124
124
|
role='button'
|
|
125
125
|
item={item}
|
|
126
|
-
classNames={
|
|
126
|
+
classNames={['p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay']}
|
|
127
127
|
aria-expanded={selected === item.id}
|
|
128
128
|
>
|
|
129
129
|
<div className='flex items-center'>
|
|
@@ -4,26 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
9
|
import React, { useCallback, useMemo, useState, useEffect } from 'react';
|
|
10
10
|
|
|
11
|
+
import { Obj, Type } from '@dxos/echo';
|
|
11
12
|
import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
|
|
12
13
|
import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
13
14
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
14
15
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
15
|
-
import {
|
|
16
|
+
import { DataType, ProjectionModel, createView } from '@dxos/schema';
|
|
16
17
|
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
17
18
|
|
|
18
19
|
import { ViewEditor, type ViewEditorProps } from './ViewEditor';
|
|
19
|
-
import translations from '../../translations';
|
|
20
|
+
import { translations } from '../../translations';
|
|
20
21
|
import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
21
22
|
|
|
22
23
|
// Type definition for debug objects exposed to tests.
|
|
23
24
|
export type ViewEditorDebugObjects = {
|
|
24
25
|
schema: EchoSchema;
|
|
25
|
-
view:
|
|
26
|
-
projection:
|
|
26
|
+
view: DataType.View;
|
|
27
|
+
projection: ProjectionModel;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
type StoryProps = Pick<ViewEditorProps, 'readonly'>;
|
|
@@ -31,8 +32,8 @@ type StoryProps = Pick<ViewEditorProps, 'readonly'>;
|
|
|
31
32
|
const DefaultStory = (props: StoryProps) => {
|
|
32
33
|
const space = useSpace();
|
|
33
34
|
const [schema, setSchema] = useState<EchoSchema>();
|
|
34
|
-
const [view, setView] = useState<
|
|
35
|
-
const [projection, setProjection] = useState<
|
|
35
|
+
const [view, setView] = useState<DataType.View>();
|
|
36
|
+
const [projection, setProjection] = useState<ProjectionModel>();
|
|
36
37
|
useAsyncEffect(async () => {
|
|
37
38
|
if (space) {
|
|
38
39
|
class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
@@ -42,8 +43,13 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
42
43
|
}) {}
|
|
43
44
|
|
|
44
45
|
const [schema] = await space.db.schemaRegistry.register([TestSchema]);
|
|
45
|
-
const view = createView({
|
|
46
|
-
|
|
46
|
+
const view = createView({
|
|
47
|
+
name: 'Test',
|
|
48
|
+
typename: schema.typename,
|
|
49
|
+
jsonSchema: toJsonSchema(TestSchema),
|
|
50
|
+
presentation: Obj.make(Type.Expando, {}),
|
|
51
|
+
});
|
|
52
|
+
const projection = new ProjectionModel(schema.jsonSchema, view.projection);
|
|
47
53
|
|
|
48
54
|
setSchema(schema);
|
|
49
55
|
setView(view);
|
|
@@ -51,7 +57,7 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
51
57
|
}
|
|
52
58
|
}, [space]);
|
|
53
59
|
|
|
54
|
-
const views = useQuery(space, Filter.type(
|
|
60
|
+
const views = useQuery(space, Filter.type(DataType.View));
|
|
55
61
|
const currentTypename = useMemo(() => view?.query?.typename, [view]);
|
|
56
62
|
const updateViewTypename = useCallback(
|
|
57
63
|
(newTypename: string) => {
|
|
@@ -6,8 +6,8 @@ import { composeStories } from '@storybook/react';
|
|
|
6
6
|
import { screen, cleanup, fireEvent, 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 {
|
|
9
|
+
import { EchoSchema, isInstanceOf } from '@dxos/echo-schema';
|
|
10
|
+
import { DataType, ProjectionModel } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import * as stories from './ViewEditor.stories';
|
|
13
13
|
import { type ViewEditorDebugObjects } from './ViewEditor.stories';
|
|
@@ -19,8 +19,8 @@ const getViewEditorDebugObjects = (): ViewEditorDebugObjects => {
|
|
|
19
19
|
const debugObjects = (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] as ViewEditorDebugObjects;
|
|
20
20
|
expect(debugObjects).toBeDefined();
|
|
21
21
|
expect(debugObjects.schema).toBeInstanceOf(EchoSchema);
|
|
22
|
-
expect(debugObjects.view).
|
|
23
|
-
expect(debugObjects.projection).toBeInstanceOf(
|
|
22
|
+
expect(isInstanceOf(DataType.View, debugObjects.view)).toBeTruthy();
|
|
23
|
+
expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
|
|
24
24
|
return debugObjects;
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -41,7 +41,7 @@ describe('ViewEditor', () => {
|
|
|
41
41
|
test('renders view editor', async () => {
|
|
42
42
|
await Default.run();
|
|
43
43
|
await waitForViewEditor();
|
|
44
|
-
expect(screen.getByText('
|
|
44
|
+
expect(screen.getByText('Typename')).toBeInTheDocument();
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
test('change property name', async () => {
|
|
@@ -58,7 +58,7 @@ describe('ViewEditor', () => {
|
|
|
58
58
|
|
|
59
59
|
fireEvent.click(screen.getByTestId('save-button'));
|
|
60
60
|
|
|
61
|
-
// Wait for the changes to propagate to both UI and schema
|
|
61
|
+
// Wait for the changes to propagate to both UI and schema.
|
|
62
62
|
await waitFor(() => {
|
|
63
63
|
expect(screen.getByText('new_property')).toBeInTheDocument();
|
|
64
64
|
});
|
|
@@ -66,18 +66,18 @@ describe('ViewEditor', () => {
|
|
|
66
66
|
// Access the live objects directly from window using symbol.
|
|
67
67
|
const debugObjects = getViewEditorDebugObjects();
|
|
68
68
|
|
|
69
|
-
// Check schema contains new_property
|
|
69
|
+
// Check schema contains new_property.
|
|
70
70
|
const schemaProperties = debugObjects.schema.jsonSchema.properties;
|
|
71
71
|
expect(schemaProperties).toBeDefined();
|
|
72
72
|
expect(schemaProperties!.new_property).toBeDefined();
|
|
73
73
|
expect(schemaProperties!.name).toBeUndefined();
|
|
74
74
|
|
|
75
|
-
// Check view contains new_property field
|
|
76
|
-
const newPropertyField = debugObjects.view.fields.find((field: any) => field.path === 'new_property');
|
|
75
|
+
// Check view contains new_property field.
|
|
76
|
+
const newPropertyField = debugObjects.view.projection.fields.find((field: any) => field.path === 'new_property');
|
|
77
77
|
expect(newPropertyField).toBeDefined();
|
|
78
78
|
expect(newPropertyField!.path).toBe('new_property');
|
|
79
79
|
|
|
80
|
-
// Check projection contains new_property
|
|
80
|
+
// Check projection contains new_property.
|
|
81
81
|
const fieldProjections = debugObjects.projection.getFieldProjections();
|
|
82
82
|
const newPropertyProjection = fieldProjections.find((proj: any) => proj.field.path === 'new_property');
|
|
83
83
|
expect(newPropertyProjection).toBeDefined();
|
|
@@ -88,25 +88,25 @@ describe('ViewEditor', () => {
|
|
|
88
88
|
await Default.run();
|
|
89
89
|
await waitForViewEditor();
|
|
90
90
|
|
|
91
|
-
// Click the add property button
|
|
91
|
+
// Click the add property button.
|
|
92
92
|
const addButton = screen.getByText('Add property');
|
|
93
93
|
fireEvent.click(addButton);
|
|
94
94
|
|
|
95
|
-
// Fill out the property field
|
|
95
|
+
// Fill out the property field.
|
|
96
96
|
const fieldInput = screen.getByPlaceholderText('Field name.');
|
|
97
97
|
fireEvent.change(fieldInput, { target: { value: 'added_property' } });
|
|
98
98
|
|
|
99
|
-
// Click the format combo box and select the first option
|
|
99
|
+
// Click the format combo box and select the first option.
|
|
100
100
|
const formatCombo = screen.getByRole('combobox');
|
|
101
101
|
fireEvent.click(formatCombo);
|
|
102
102
|
|
|
103
103
|
const firstOption = screen.getAllByRole('option')[0];
|
|
104
104
|
fireEvent.click(firstOption);
|
|
105
105
|
|
|
106
|
-
// Save the changes
|
|
106
|
+
// Save the changes.
|
|
107
107
|
fireEvent.click(screen.getByTestId('save-button'));
|
|
108
108
|
|
|
109
|
-
// Wait for the changes to propagate
|
|
109
|
+
// Wait for the changes to propagate.
|
|
110
110
|
await waitFor(() => {
|
|
111
111
|
expect(screen.getByText('added_property')).toBeInTheDocument();
|
|
112
112
|
});
|
|
@@ -114,17 +114,19 @@ describe('ViewEditor', () => {
|
|
|
114
114
|
// Access the live objects directly from window using symbol.
|
|
115
115
|
const debugObjects = getViewEditorDebugObjects();
|
|
116
116
|
|
|
117
|
-
// Check schema contains added_property
|
|
117
|
+
// Check schema contains added_property.
|
|
118
118
|
const schemaProperties = debugObjects.schema.jsonSchema.properties;
|
|
119
119
|
expect(schemaProperties).toBeDefined();
|
|
120
120
|
expect(schemaProperties!.added_property).toBeDefined();
|
|
121
121
|
|
|
122
|
-
// Check view contains added_property field
|
|
123
|
-
const addedPropertyField = debugObjects.view.fields.find(
|
|
122
|
+
// Check view contains added_property field.
|
|
123
|
+
const addedPropertyField = debugObjects.view.projection.fields.find(
|
|
124
|
+
(field: any) => field.path === 'added_property',
|
|
125
|
+
);
|
|
124
126
|
expect(addedPropertyField).toBeDefined();
|
|
125
127
|
expect(addedPropertyField!.path).toBe('added_property');
|
|
126
128
|
|
|
127
|
-
// Check projection contains added_property
|
|
129
|
+
// Check projection contains added_property.
|
|
128
130
|
const fieldProjections = debugObjects.projection.getFieldProjections();
|
|
129
131
|
const addedPropertyProjection = fieldProjections.find((proj: any) => proj.field.path === 'added_property');
|
|
130
132
|
expect(addedPropertyProjection).toBeDefined();
|
|
@@ -135,7 +137,7 @@ describe('ViewEditor', () => {
|
|
|
135
137
|
await Default.run();
|
|
136
138
|
await waitForViewEditor();
|
|
137
139
|
|
|
138
|
-
// Find the delete button for the 'name' property
|
|
140
|
+
// Find the delete button for the 'name' property.
|
|
139
141
|
const nameProperty = screen.getByText('name');
|
|
140
142
|
const propertyRow = nameProperty.closest('[role="listitem"]');
|
|
141
143
|
const deleteButton = propertyRow?.querySelector('button:last-child');
|
|
@@ -144,10 +146,10 @@ describe('ViewEditor', () => {
|
|
|
144
146
|
throw new Error('Delete button not found');
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
// Click the delete button
|
|
149
|
+
// Click the delete button.
|
|
148
150
|
fireEvent.click(deleteButton);
|
|
149
151
|
|
|
150
|
-
// Wait for the changes to propagate
|
|
152
|
+
// Wait for the changes to propagate.
|
|
151
153
|
await waitFor(() => {
|
|
152
154
|
expect(screen.queryByText('name')).not.toBeInTheDocument();
|
|
153
155
|
});
|
|
@@ -155,16 +157,16 @@ describe('ViewEditor', () => {
|
|
|
155
157
|
// Access the live objects directly from window using symbol.
|
|
156
158
|
const debugObjects = getViewEditorDebugObjects();
|
|
157
159
|
|
|
158
|
-
// Check schema no longer contains the name property
|
|
160
|
+
// Check schema no longer contains the name property.
|
|
159
161
|
const schemaProperties = debugObjects.schema.jsonSchema.properties;
|
|
160
162
|
expect(schemaProperties).toBeDefined();
|
|
161
163
|
expect(schemaProperties!.name).toBeUndefined();
|
|
162
164
|
|
|
163
|
-
// Check view no longer contains the name field
|
|
164
|
-
const nameField = debugObjects.view.fields.find((field: any) => field.path === 'name');
|
|
165
|
+
// Check view no longer contains the name field.
|
|
166
|
+
const nameField = debugObjects.view.projection.fields.find((field: any) => field.path === 'name');
|
|
165
167
|
expect(nameField).toBeUndefined();
|
|
166
168
|
|
|
167
|
-
// Check projection no longer contains the name property
|
|
169
|
+
// Check projection no longer contains the name property.
|
|
168
170
|
const fieldProjections = debugObjects.projection.getFieldProjections();
|
|
169
171
|
const nameProjection = fieldProjections.find((proj: any) => proj.field.path === 'name');
|
|
170
172
|
expect(nameProjection).toBeUndefined();
|
|
@@ -174,11 +176,11 @@ describe('ViewEditor', () => {
|
|
|
174
176
|
await Default.run();
|
|
175
177
|
await waitForViewEditor();
|
|
176
178
|
|
|
177
|
-
// Click the first hide button
|
|
179
|
+
// Click the first hide button.
|
|
178
180
|
const hideButtons = screen.getAllByTestId('hide-field-button');
|
|
179
181
|
fireEvent.click(hideButtons[0]);
|
|
180
182
|
|
|
181
|
-
// Check that hiddenFields is non-empty
|
|
183
|
+
// Check that hiddenFields is non-empty.
|
|
182
184
|
await waitFor(() => {
|
|
183
185
|
expect(screen.getByTestId('show-field-button')).toBeInTheDocument();
|
|
184
186
|
});
|
|
@@ -186,19 +188,19 @@ describe('ViewEditor', () => {
|
|
|
186
188
|
// Access the live objects directly from window using symbol.
|
|
187
189
|
const debugObjects = getViewEditorDebugObjects();
|
|
188
190
|
|
|
189
|
-
// Check that hiddenFields is non-empty
|
|
190
|
-
expect(debugObjects.view.hiddenFields).toBeDefined();
|
|
191
|
-
expect(debugObjects.view.hiddenFields!.length).toBeGreaterThan(0);
|
|
191
|
+
// Check that hiddenFields is non-empty.
|
|
192
|
+
expect(debugObjects.view.projection.hiddenFields).toBeDefined();
|
|
193
|
+
expect(debugObjects.view.projection.hiddenFields!.length).toBeGreaterThan(0);
|
|
192
194
|
|
|
193
195
|
// Click the show button
|
|
194
196
|
fireEvent.click(screen.getByTestId('show-field-button'));
|
|
195
197
|
|
|
196
|
-
// Wait for the data to update and check that hiddenFields is empty
|
|
198
|
+
// Wait for the data to update and check that hiddenFields is empty.
|
|
197
199
|
await waitFor(() => {
|
|
198
|
-
expect(debugObjects.view.hiddenFields!.length).toBe(0);
|
|
200
|
+
expect(debugObjects.view.projection.hiddenFields!.length).toBe(0);
|
|
199
201
|
});
|
|
200
202
|
|
|
201
|
-
// Also verify that the field is back in the visible fields list
|
|
202
|
-
expect(debugObjects.view.fields.length).toBeGreaterThan(0);
|
|
203
|
+
// Also verify that the field is back in the visible fields list.
|
|
204
|
+
expect(debugObjects.view.projection.fields.length).toBeGreaterThan(0);
|
|
203
205
|
});
|
|
204
206
|
});
|