@dxos/react-ui-form 0.8.3-main.672df60 → 0.8.3-staging.0fa589b
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 +190 -173
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +150 -133
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +190 -173
- 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.d.ts +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +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 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +5 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +6 -14
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -2
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +3 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +30 -3
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +6 -4
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts.map +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.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +7 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +28 -26
- package/src/components/ControlSection/ControlSection.stories.tsx +15 -3
- package/src/components/ControlSection/ControlSection.tsx +11 -9
- package/src/components/FieldEditor/FieldEditor.stories.tsx +25 -5
- package/src/components/FieldEditor/FieldEditor.test.tsx +21 -13
- package/src/components/FieldEditor/FieldEditor.tsx +11 -2
- package/src/components/Form/ArrayField.tsx +1 -1
- package/src/components/Form/Defaults.tsx +5 -25
- package/src/components/Form/Form.stories.tsx +5 -5
- package/src/components/Form/Form.tsx +44 -49
- package/src/components/Form/FormActions.tsx +17 -2
- package/src/components/Form/FormContent.tsx +46 -46
- package/src/components/Form/Input.tsx +13 -8
- package/src/components/Form/RefField.tsx +1 -5
- package/src/components/Form/custom/GeoPointInput.tsx +1 -3
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +7 -5
- package/src/components/Form/custom/SelectOptionsInput.tsx +1 -3
- package/src/components/Form/custom/TupleInput.tsx +1 -3
- package/src/components/ViewEditor/ViewEditor.stories.tsx +25 -9
- package/src/components/ViewEditor/ViewEditor.test.tsx +80 -42
- package/src/components/ViewEditor/ViewEditor.tsx +66 -66
- package/src/components/testing.tsx +12 -7
- package/src/translations.ts +4 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react';
|
|
2
2
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
export declare const TestPanel: ({ classNames, children }: ThemedClassName<PropsWithChildren>) => React.JSX.Element;
|
|
4
3
|
export declare const TestLayout: ({ classNames, json, children }: ThemedClassName<PropsWithChildren<{
|
|
5
4
|
json?: any;
|
|
6
5
|
}>>) => React.JSX.Element;
|
|
6
|
+
export declare const TestPanel: ({ classNames, children }: ThemedClassName<PropsWithChildren>) => React.JSX.Element;
|
|
7
|
+
export declare const VIEW_EDITOR_DEBUG_SYMBOL: unique symbol;
|
|
8
|
+
export declare const FIELD_EDITOR_DEBUG_SYMBOL: unique symbol;
|
|
7
9
|
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,eAAO,MAAM,UAAU,GAAI,gCAAgC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,sBAK5G,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,sBAErF,CAAC;AAGF,eAAO,MAAM,wBAAwB,eAA4B,CAAC;AAClE,eAAO,MAAM,yBAAyB,eAA6B,CAAC"}
|
|
@@ -23,6 +23,7 @@ declare const _default: {
|
|
|
23
23
|
'format date-time': string;
|
|
24
24
|
'format did': string;
|
|
25
25
|
'format duration': string;
|
|
26
|
+
'format dxn': string;
|
|
26
27
|
'format email': string;
|
|
27
28
|
'format formula': string;
|
|
28
29
|
'format hostname': string;
|
|
@@ -35,12 +36,14 @@ declare const _default: {
|
|
|
35
36
|
'format regex': string;
|
|
36
37
|
'format string': string;
|
|
37
38
|
'format single-select': string;
|
|
39
|
+
'format multi-select': string;
|
|
38
40
|
'format text': string;
|
|
39
41
|
'format time': string;
|
|
40
42
|
'format timestamp': string;
|
|
41
43
|
'format user': string;
|
|
42
44
|
'format number': string;
|
|
43
45
|
'format uri': string;
|
|
46
|
+
'format url': string;
|
|
44
47
|
'format uuid': string;
|
|
45
48
|
'placeholder latitude': string;
|
|
46
49
|
'placeholder longitude': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBAuEE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-form",
|
|
3
|
-
"version": "0.8.3-
|
|
3
|
+
"version": "0.8.3-staging.0fa589b",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -29,23 +29,25 @@
|
|
|
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/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/react-ui-
|
|
44
|
-
"@dxos/react-ui-
|
|
45
|
-
"@dxos/react-
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
32
|
+
"@dxos/debug": "0.8.3-staging.0fa589b",
|
|
33
|
+
"@dxos/echo-db": "0.8.3-staging.0fa589b",
|
|
34
|
+
"@dxos/async": "0.8.3-staging.0fa589b",
|
|
35
|
+
"@dxos/echo": "0.8.3-staging.0fa589b",
|
|
36
|
+
"@dxos/echo-schema": "0.8.3-staging.0fa589b",
|
|
37
|
+
"@dxos/effect": "0.8.3-staging.0fa589b",
|
|
38
|
+
"@dxos/invariant": "0.8.3-staging.0fa589b",
|
|
39
|
+
"@dxos/keys": "0.8.3-staging.0fa589b",
|
|
40
|
+
"@dxos/lit-ui": "0.8.3-staging.0fa589b",
|
|
41
|
+
"@dxos/live-object": "0.8.3-staging.0fa589b",
|
|
42
|
+
"@dxos/log": "0.8.3-staging.0fa589b",
|
|
43
|
+
"@dxos/react-ui-pickers": "0.8.3-staging.0fa589b",
|
|
44
|
+
"@dxos/react-ui-list": "0.8.3-staging.0fa589b",
|
|
45
|
+
"@dxos/react-client": "0.8.3-staging.0fa589b",
|
|
46
|
+
"@dxos/react-ui-stack": "0.8.3-staging.0fa589b",
|
|
47
|
+
"@dxos/react-ui-tag-picker": "0.8.3-staging.0fa589b",
|
|
48
|
+
"@dxos/react-ui-types": "0.8.3-staging.0fa589b",
|
|
49
|
+
"@dxos/schema": "0.8.3-staging.0fa589b",
|
|
50
|
+
"@dxos/util": "0.8.3-staging.0fa589b"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
51
53
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -57,20 +59,20 @@
|
|
|
57
59
|
"react": "~18.2.0",
|
|
58
60
|
"react-dom": "~18.2.0",
|
|
59
61
|
"vite": "5.4.7",
|
|
60
|
-
"@dxos/echo-signals": "0.8.3-
|
|
61
|
-
"@dxos/random": "0.8.3-
|
|
62
|
-
"@dxos/react-ui": "0.8.3-
|
|
63
|
-
"@dxos/react-ui
|
|
64
|
-
"@dxos/
|
|
65
|
-
"@dxos/
|
|
62
|
+
"@dxos/echo-signals": "0.8.3-staging.0fa589b",
|
|
63
|
+
"@dxos/random": "0.8.3-staging.0fa589b",
|
|
64
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.3-staging.0fa589b",
|
|
65
|
+
"@dxos/react-ui": "0.8.3-staging.0fa589b",
|
|
66
|
+
"@dxos/react-ui-theme": "0.8.3-staging.0fa589b",
|
|
67
|
+
"@dxos/storybook-utils": "0.8.3-staging.0fa589b"
|
|
66
68
|
},
|
|
67
69
|
"peerDependencies": {
|
|
68
70
|
"@phosphor-icons/react": "^2.1.5",
|
|
69
71
|
"effect": "3.13.3",
|
|
70
72
|
"react": "~18.2.0",
|
|
71
73
|
"react-dom": "~18.2.0",
|
|
72
|
-
"@dxos/react-ui": "0.8.3-
|
|
73
|
-
"@dxos/react-ui
|
|
74
|
+
"@dxos/react-ui-theme": "0.8.3-staging.0fa589b",
|
|
75
|
+
"@dxos/react-ui": "0.8.3-staging.0fa589b"
|
|
74
76
|
},
|
|
75
77
|
"publishConfig": {
|
|
76
78
|
"access": "public"
|
|
@@ -5,16 +5,25 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import React from 'react';
|
|
8
9
|
|
|
10
|
+
import { StackItem } from '@dxos/react-ui-stack';
|
|
9
11
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
10
12
|
|
|
11
|
-
import { ControlSection, type ControlSectionProps } from './ControlSection';
|
|
13
|
+
import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
|
|
12
14
|
import translations from '../../translations';
|
|
13
15
|
|
|
14
16
|
const meta: Meta<ControlSectionProps> = {
|
|
15
17
|
title: 'ui/react-ui-form/ControlSection',
|
|
16
18
|
component: ControlSection,
|
|
17
|
-
|
|
19
|
+
render: (args) => (
|
|
20
|
+
<StackItem.Content classNames='w-[40rem]'>
|
|
21
|
+
<ControlPage>
|
|
22
|
+
<ControlSection {...args} />
|
|
23
|
+
</ControlPage>
|
|
24
|
+
</StackItem.Content>
|
|
25
|
+
),
|
|
26
|
+
decorators: [withLayout({ fullscreen: true, classNames: 'justify-center' }), withTheme],
|
|
18
27
|
parameters: {
|
|
19
28
|
translations,
|
|
20
29
|
},
|
|
@@ -25,5 +34,8 @@ export default meta;
|
|
|
25
34
|
type Story = StoryObj<ControlSectionProps>;
|
|
26
35
|
|
|
27
36
|
export const Default: Story = {
|
|
28
|
-
args: {
|
|
37
|
+
args: {
|
|
38
|
+
title: 'Control Section',
|
|
39
|
+
description: 'This is a control section',
|
|
40
|
+
},
|
|
29
41
|
};
|
|
@@ -7,8 +7,8 @@ import React, { type ComponentPropsWithoutRef, type PropsWithChildren } from 're
|
|
|
7
7
|
import {
|
|
8
8
|
Button,
|
|
9
9
|
type ButtonProps,
|
|
10
|
-
type Label,
|
|
11
10
|
Input,
|
|
11
|
+
type Label,
|
|
12
12
|
type ThemedClassName,
|
|
13
13
|
toLocalizedString,
|
|
14
14
|
useTranslation,
|
|
@@ -21,7 +21,7 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
|
21
21
|
|
|
22
22
|
export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
|
|
23
23
|
return (
|
|
24
|
-
<div role='none' className={mx('pli-
|
|
24
|
+
<div role='none' className={mx('pli-cardSpacingInline', classNames)} {...props}>
|
|
25
25
|
{children}
|
|
26
26
|
</div>
|
|
27
27
|
);
|
|
@@ -45,9 +45,11 @@ export const ControlSectionHeading = ({ title, description }: Omit<ControlSectio
|
|
|
45
45
|
const { t } = useTranslation(translationKey);
|
|
46
46
|
return (
|
|
47
47
|
<>
|
|
48
|
-
<h2 className='pli-
|
|
48
|
+
<h2 className='pli-trimMd container-max-width text-xl mbs-trimLg mbe-trimMd'>{toLocalizedString(title, t)}</h2>
|
|
49
49
|
{description && (
|
|
50
|
-
<p className='pli-
|
|
50
|
+
<p className='pli-trimMd mlb-trimMd container-max-width text-description'>
|
|
51
|
+
{toLocalizedString(description, t)}
|
|
52
|
+
</p>
|
|
51
53
|
)}
|
|
52
54
|
</>
|
|
53
55
|
);
|
|
@@ -62,7 +64,7 @@ export type ControlGroupProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
|
62
64
|
export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
|
|
63
65
|
<div
|
|
64
66
|
role='none'
|
|
65
|
-
className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-
|
|
67
|
+
className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd', classNames)}
|
|
66
68
|
>
|
|
67
69
|
{children}
|
|
68
70
|
</div>
|
|
@@ -71,7 +73,7 @@ export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
|
|
|
71
73
|
export const ControlFrame = ({ children }: ControlGroupProps) => (
|
|
72
74
|
<div
|
|
73
75
|
role='none'
|
|
74
|
-
className='p-
|
|
76
|
+
className='p-trimMd container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd border border-separator rounded-md'
|
|
75
77
|
>
|
|
76
78
|
{children}
|
|
77
79
|
</div>
|
|
@@ -83,10 +85,10 @@ export type ControlItemProps = PropsWithChildren<{
|
|
|
83
85
|
}>;
|
|
84
86
|
|
|
85
87
|
export const controlItemClasses =
|
|
86
|
-
'
|
|
88
|
+
'pli-trimMd plb-trimMd container-max-width grid md:col-span-2 grid-cols-subgrid items-center gap-trimSm border border-separator rounded-md *:first:!mbs-0 *:last:!mbe-0';
|
|
87
89
|
|
|
88
90
|
const controlItemTitleClasses = 'text-lg font-normal';
|
|
89
|
-
const controlItemDescriptionClasses = 'text-base mlb-
|
|
91
|
+
const controlItemDescriptionClasses = 'text-base mlb-trimSm md:mbe-0 text-description';
|
|
90
92
|
|
|
91
93
|
export const ControlItem = ({ title, description, children }: ControlItemProps) => {
|
|
92
94
|
const { t } = useTranslation(translationKey);
|
|
@@ -130,7 +132,7 @@ export const ControlFrameItem = ({ title, description, children }: ControlItemPr
|
|
|
130
132
|
return (
|
|
131
133
|
<div role='group' className='min-is-0'>
|
|
132
134
|
<h3 className='text-lg mbe-2'>{toLocalizedString(title, t)}</h3>
|
|
133
|
-
{description && <p className='mlb-
|
|
135
|
+
{description && <p className='mlb-trimSm md:mbe-0 text-description'>{toLocalizedString(description, t)}</p>}
|
|
134
136
|
{children}
|
|
135
137
|
</div>
|
|
136
138
|
);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
-
import React from 'react';
|
|
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';
|
|
@@ -15,19 +15,39 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
15
15
|
|
|
16
16
|
import { FieldEditor, type FieldEditorProps } from './FieldEditor';
|
|
17
17
|
import translations from '../../translations';
|
|
18
|
-
import { TestLayout, TestPanel } from '../testing';
|
|
18
|
+
import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
19
|
+
|
|
20
|
+
// Type definition for debug objects exposed to tests.
|
|
21
|
+
export type FieldEditorDebugObjects = {
|
|
22
|
+
props: FieldEditorProps;
|
|
23
|
+
projection: ViewProjection;
|
|
24
|
+
};
|
|
19
25
|
|
|
20
26
|
type StoryProps = FieldEditorProps;
|
|
21
27
|
|
|
22
28
|
const DefaultStory = (props: FieldEditorProps) => {
|
|
29
|
+
const projection = useMemo(() => new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView), []);
|
|
23
30
|
const handleComplete: FieldEditorProps['onSave'] = () => {
|
|
24
31
|
log.info('onClose', { props });
|
|
25
32
|
};
|
|
26
33
|
|
|
34
|
+
// Expose objects on window for test access.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (typeof window !== 'undefined') {
|
|
37
|
+
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = { props, projection } satisfies FieldEditorDebugObjects;
|
|
38
|
+
}
|
|
39
|
+
}, [props, projection]);
|
|
40
|
+
|
|
41
|
+
// NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
|
|
42
|
+
const json = useMemo(
|
|
43
|
+
() => JSON.parse(JSON.stringify({ props, projection })),
|
|
44
|
+
[JSON.stringify(props), JSON.stringify(projection)],
|
|
45
|
+
);
|
|
46
|
+
|
|
27
47
|
return (
|
|
28
|
-
<TestLayout json={
|
|
48
|
+
<TestLayout json={json}>
|
|
29
49
|
<TestPanel>
|
|
30
|
-
<FieldEditor {...props} onSave={handleComplete} />
|
|
50
|
+
<FieldEditor {...props} projection={projection} onSave={handleComplete} />
|
|
31
51
|
</TestPanel>
|
|
32
52
|
</TestLayout>
|
|
33
53
|
);
|
|
@@ -49,8 +69,8 @@ type Story = StoryObj<StoryProps>;
|
|
|
49
69
|
|
|
50
70
|
export const Default: Story = {
|
|
51
71
|
args: {
|
|
52
|
-
projection: new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView),
|
|
53
72
|
view: testView,
|
|
54
73
|
field: testView.fields[0],
|
|
55
74
|
},
|
|
75
|
+
parameters: { controls: { disabled: true } },
|
|
56
76
|
};
|
|
@@ -6,10 +6,22 @@ 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 { ViewProjection } from '@dxos/schema';
|
|
10
|
+
|
|
9
11
|
import * as stories from './FieldEditor.stories';
|
|
12
|
+
import { type FieldEditorDebugObjects } from './FieldEditor.stories';
|
|
13
|
+
import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
10
14
|
|
|
11
15
|
const { Default } = composeStories(stories);
|
|
12
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
|
+
|
|
13
25
|
describe('FieldEditor', () => {
|
|
14
26
|
afterEach(() => {
|
|
15
27
|
cleanup();
|
|
@@ -21,12 +33,8 @@ describe('FieldEditor', () => {
|
|
|
21
33
|
expect(screen.getByText('Type format')).toBeInTheDocument();
|
|
22
34
|
expect(screen.getByText('String')).toBeInTheDocument();
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const name = debug.props.projection._schema.properties.name;
|
|
27
|
-
expect(name.type).toBe('string');
|
|
28
|
-
expect(name.description).toBe('Full name.');
|
|
29
|
-
}
|
|
36
|
+
// Verify the initial field shows as String type
|
|
37
|
+
expect(screen.getByText('String')).toBeInTheDocument();
|
|
30
38
|
|
|
31
39
|
fireEvent.click(screen.getByRole('combobox'));
|
|
32
40
|
const options = screen.getAllByRole('option');
|
|
@@ -38,12 +46,12 @@ describe('FieldEditor', () => {
|
|
|
38
46
|
fireEvent.click(screen.getByTestId('save-button'));
|
|
39
47
|
});
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
// Access the live objects directly from window using symbol.
|
|
50
|
+
const debugObjects = getFieldEditorDebugObjects();
|
|
51
|
+
|
|
52
|
+
const name = debugObjects.projection.schema.properties!.name;
|
|
53
|
+
expect(name.type).toBe('number');
|
|
54
|
+
expect(name.format).toBe('number');
|
|
55
|
+
expect(name.description).toBe('Full name.');
|
|
48
56
|
});
|
|
49
57
|
});
|
|
@@ -29,12 +29,20 @@ export type FieldEditorProps = {
|
|
|
29
29
|
registry?: SchemaRegistry;
|
|
30
30
|
onSave: () => void;
|
|
31
31
|
onCancel?: () => void;
|
|
32
|
-
}
|
|
32
|
+
} & Pick<FormProps<any>, 'outerSpacing'>;
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Displays a Form representing the metadata for a given `Field` and `View`.
|
|
36
36
|
*/
|
|
37
|
-
export const FieldEditor = ({
|
|
37
|
+
export const FieldEditor = ({
|
|
38
|
+
view,
|
|
39
|
+
projection,
|
|
40
|
+
field,
|
|
41
|
+
registry,
|
|
42
|
+
onSave,
|
|
43
|
+
onCancel,
|
|
44
|
+
outerSpacing,
|
|
45
|
+
}: FieldEditorProps) => {
|
|
38
46
|
const { t } = useTranslation(translationKey);
|
|
39
47
|
const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
|
|
40
48
|
useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
|
|
@@ -189,6 +197,7 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
189
197
|
onSave={handleSave}
|
|
190
198
|
onCancel={handleCancel}
|
|
191
199
|
Custom={custom}
|
|
200
|
+
outerSpacing={outerSpacing}
|
|
192
201
|
/>
|
|
193
202
|
);
|
|
194
203
|
};
|
|
@@ -26,11 +26,7 @@ export const TextInput = ({
|
|
|
26
26
|
<p>{getValue() ?? ''}</p>
|
|
27
27
|
) : (
|
|
28
28
|
<Input.Root validationValence={status}>
|
|
29
|
-
{!inputOnly &&
|
|
30
|
-
<InputHeader error={error}>
|
|
31
|
-
<Input.Label>{label}</Input.Label>
|
|
32
|
-
</InputHeader>
|
|
33
|
-
)}
|
|
29
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
34
30
|
{disabled ? (
|
|
35
31
|
<p>{getValue() ?? ''}</p>
|
|
36
32
|
) : (
|
|
@@ -64,11 +60,7 @@ export const NumberInput = ({
|
|
|
64
60
|
<p>{getValue() ?? ''}</p>
|
|
65
61
|
) : (
|
|
66
62
|
<Input.Root validationValence={status}>
|
|
67
|
-
{!inputOnly &&
|
|
68
|
-
<InputHeader error={error}>
|
|
69
|
-
<Input.Label>{label}</Input.Label>
|
|
70
|
-
</InputHeader>
|
|
71
|
-
)}
|
|
63
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
72
64
|
{disabled ? (
|
|
73
65
|
<p>{getValue() ?? ''}</p>
|
|
74
66
|
) : (
|
|
@@ -93,11 +85,7 @@ export const BooleanInput = ({ type, label, inputOnly, getStatus, getValue, onVa
|
|
|
93
85
|
|
|
94
86
|
return (
|
|
95
87
|
<Input.Root validationValence={status}>
|
|
96
|
-
{!inputOnly &&
|
|
97
|
-
<InputHeader error={error}>
|
|
98
|
-
<Input.Label>{label}</Input.Label>
|
|
99
|
-
</InputHeader>
|
|
100
|
-
)}
|
|
88
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
101
89
|
{disabled ? (
|
|
102
90
|
<p>{t(checked ? 'boolean input true value' : 'boolean input false value')}</p>
|
|
103
91
|
) : (
|
|
@@ -130,11 +118,7 @@ export const SelectInput = ({
|
|
|
130
118
|
|
|
131
119
|
return disabled && !value ? null : (
|
|
132
120
|
<Input.Root validationValence={status}>
|
|
133
|
-
{!inputOnly &&
|
|
134
|
-
<InputHeader error={error}>
|
|
135
|
-
<Input.Label>{label}</Input.Label>
|
|
136
|
-
</InputHeader>
|
|
137
|
-
)}
|
|
121
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
138
122
|
{disabled ? (
|
|
139
123
|
<p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
|
|
140
124
|
) : (
|
|
@@ -197,11 +181,7 @@ export const MarkdownInput = ({
|
|
|
197
181
|
|
|
198
182
|
return (
|
|
199
183
|
<Input.Root validationValence={status}>
|
|
200
|
-
{!inputOnly &&
|
|
201
|
-
<InputHeader error={error}>
|
|
202
|
-
<Input.Label>{label}</Input.Label>
|
|
203
|
-
</InputHeader>
|
|
204
|
-
)}
|
|
184
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
205
185
|
{disabled ? (
|
|
206
186
|
<p>{getValue() ?? ''}</p>
|
|
207
187
|
) : (
|
|
@@ -13,7 +13,7 @@ import { type BaseObject, Expando, Format, getObjectDXN, Ref, type TypeAnnotatio
|
|
|
13
13
|
import { live } from '@dxos/live-object';
|
|
14
14
|
import { withSurfaceVariantsLayout } from '@dxos/react-ui/testing';
|
|
15
15
|
import { Testing } from '@dxos/schema/testing';
|
|
16
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
16
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
17
17
|
|
|
18
18
|
import { SelectInput } from './Defaults';
|
|
19
19
|
import { Form, type FormProps } from './Form';
|
|
@@ -66,9 +66,9 @@ const DebugStory = <T extends BaseObject>({ schema, values: initialValues, ...pr
|
|
|
66
66
|
|
|
67
67
|
const meta: Meta<StoryProps<any>> = {
|
|
68
68
|
title: 'ui/react-ui-form/Form',
|
|
69
|
-
component: Form
|
|
69
|
+
component: Form<any>,
|
|
70
70
|
render: DebugStory,
|
|
71
|
-
decorators: [withTheme],
|
|
71
|
+
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
72
72
|
parameters: {
|
|
73
73
|
translations,
|
|
74
74
|
},
|
|
@@ -100,7 +100,7 @@ export const Default: Story<TestType> = {
|
|
|
100
100
|
},
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
export const Organization: Story<Testing.
|
|
103
|
+
export const Organization: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
|
|
104
104
|
args: {
|
|
105
105
|
schema: Testing.OrganizationSchema,
|
|
106
106
|
values: {
|
|
@@ -111,7 +111,7 @@ export const Organization: Story<Testing.Organization> = {
|
|
|
111
111
|
},
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
export const OrganizationAutoSave: Story<Testing.
|
|
114
|
+
export const OrganizationAutoSave: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
|
|
115
115
|
args: {
|
|
116
116
|
schema: Testing.OrganizationSchema,
|
|
117
117
|
values: {
|
|
@@ -7,14 +7,14 @@ import React, { type ReactElement, useEffect, useMemo, useRef } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
|
|
9
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
-
import {
|
|
10
|
+
import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
|
|
11
11
|
import { type SchemaProperty } from '@dxos/schema';
|
|
12
12
|
|
|
13
|
-
import { FormActions } from './FormActions';
|
|
14
|
-
import { FormFields } from './FormContent';
|
|
13
|
+
import { FormActions, type FormOuterSpacing } from './FormActions';
|
|
14
|
+
import { FormFields, type FormFieldsProps } from './FormContent';
|
|
15
15
|
import { FormProvider } from './FormContext';
|
|
16
16
|
import { type InputProps, type InputComponent } from './Input';
|
|
17
|
-
import { type FormOptions
|
|
17
|
+
import { type FormOptions } from '../../hooks';
|
|
18
18
|
|
|
19
19
|
export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
20
20
|
|
|
@@ -26,53 +26,39 @@ export type ComponentLookup = (args: {
|
|
|
26
26
|
|
|
27
27
|
export type CustomInputMap = Partial<Record<string, InputComponent>>;
|
|
28
28
|
|
|
29
|
-
export type FormProps<T extends BaseObject> = ThemedClassName<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
autoSave?: boolean;
|
|
43
|
-
testId?: string;
|
|
44
|
-
onCancel?: () => void;
|
|
45
|
-
onQueryRefOptions?: QueryRefOptions;
|
|
46
|
-
lookupComponent?: ComponentLookup;
|
|
47
|
-
/**
|
|
48
|
-
* Map of custom renderers for specific properties.
|
|
49
|
-
* Prefer lookupComponent for plugin specific input surfaces.
|
|
50
|
-
*/
|
|
51
|
-
Custom?: CustomInputMap;
|
|
52
|
-
} & Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'>
|
|
53
|
-
>;
|
|
29
|
+
export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
30
|
+
values: Partial<T>;
|
|
31
|
+
// TODO(burdon): Autofocus first input.
|
|
32
|
+
autoFocus?: boolean;
|
|
33
|
+
// TODO(burdon): Change to JsonPath includes/excludes.
|
|
34
|
+
filter?: PropsFilter<T>;
|
|
35
|
+
sort?: PropertyKey<T>[];
|
|
36
|
+
autoSave?: boolean;
|
|
37
|
+
outerSpacing?: FormOuterSpacing;
|
|
38
|
+
onCancel?: () => void;
|
|
39
|
+
}> &
|
|
40
|
+
Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'> &
|
|
41
|
+
FormFieldsProps;
|
|
54
42
|
|
|
55
43
|
export const Form = <T extends BaseObject>({
|
|
56
44
|
classNames,
|
|
57
|
-
|
|
45
|
+
testId,
|
|
58
46
|
values: initialValues,
|
|
59
|
-
path = [],
|
|
60
47
|
autoFocus,
|
|
61
48
|
readonly,
|
|
62
|
-
filter,
|
|
63
|
-
sort,
|
|
64
49
|
autoSave,
|
|
65
|
-
|
|
50
|
+
outerSpacing = true,
|
|
51
|
+
onCancel,
|
|
52
|
+
schema,
|
|
66
53
|
onValuesChanged,
|
|
67
54
|
onValidate,
|
|
68
55
|
onSave,
|
|
69
|
-
|
|
70
|
-
onQueryRefOptions,
|
|
71
|
-
lookupComponent,
|
|
72
|
-
Custom,
|
|
56
|
+
...props
|
|
73
57
|
}: FormProps<T>) => {
|
|
74
58
|
const formRef = useRef<HTMLDivElement>(null);
|
|
75
|
-
|
|
59
|
+
|
|
60
|
+
// TODO(burdon): Rename.
|
|
61
|
+
const handleValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
|
|
76
62
|
|
|
77
63
|
// Focus the first input element within this form.
|
|
78
64
|
useEffect(() => {
|
|
@@ -92,21 +78,30 @@ export const Form = <T extends BaseObject>({
|
|
|
92
78
|
initialValues={initialValues}
|
|
93
79
|
onValuesChanged={onValuesChanged}
|
|
94
80
|
onValidate={onValidate}
|
|
95
|
-
onValid={
|
|
81
|
+
onValid={handleValid}
|
|
96
82
|
onSave={onSave}
|
|
97
83
|
>
|
|
98
|
-
<div
|
|
84
|
+
<div role='none' className='contents' data-testid={testId}>
|
|
99
85
|
<FormFields
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
{...props}
|
|
87
|
+
ref={formRef}
|
|
88
|
+
classNames={[
|
|
89
|
+
outerSpacing === 'blockStart-0'
|
|
90
|
+
? 'pli-cardSpacingInline mbe-cardSpacingBlock [&>.mbs-inputSpacingBlock:first-child]:!mbs-0'
|
|
91
|
+
: outerSpacing === 'scroll-fields'
|
|
92
|
+
? 'pli-cardSpacingInline pbe-cardSpacingBlock'
|
|
93
|
+
: outerSpacing
|
|
94
|
+
? cardSpacing
|
|
95
|
+
: false,
|
|
96
|
+
outerSpacing === 'scroll-fields' && cardDialogOverflow,
|
|
97
|
+
classNames,
|
|
98
|
+
]}
|
|
102
99
|
readonly={readonly}
|
|
103
|
-
|
|
104
|
-
sort={sort}
|
|
105
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
106
|
-
lookupComponent={lookupComponent}
|
|
107
|
-
Custom={Custom}
|
|
100
|
+
schema={schema}
|
|
108
101
|
/>
|
|
109
|
-
{(onCancel || onSave) && !autoSave &&
|
|
102
|
+
{(onCancel || onSave) && !autoSave && (
|
|
103
|
+
<FormActions readonly={readonly} onCancel={onCancel} outerSpacing={outerSpacing} />
|
|
104
|
+
)}
|
|
110
105
|
</div>
|
|
111
106
|
</FormProvider>
|
|
112
107
|
);
|