@dxos/react-ui-form 0.8.4-main.bc674ce → 0.8.4-main.c351d160a8

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 (82) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +538 -458
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node-esm/index.mjs +538 -458
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -3
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Form/Form.d.ts +13 -13
  12. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.stories.d.ts +3 -3
  14. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormFieldComponent.d.ts +0 -1
  17. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +2 -2
  19. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormFieldSet.d.ts +9 -10
  21. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/fields/RefField.d.ts +6 -4
  24. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
  25. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  26. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Settings/Settings.d.ts +56 -0
  28. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  29. package/dist/types/src/components/{ControlSection/ControlSection.stories.d.ts → Settings/Settings.stories.d.ts} +2 -8
  30. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  31. package/dist/types/src/components/Settings/index.d.ts +2 -0
  32. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  33. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +5 -5
  34. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  35. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +2 -1
  36. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/index.d.ts +1 -1
  38. package/dist/types/src/components/index.d.ts.map +1 -1
  39. package/dist/types/src/components/testing.d.ts.map +1 -1
  40. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
  41. package/dist/types/src/util/index.d.ts +0 -1
  42. package/dist/types/src/util/index.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +35 -37
  45. package/src/components/FieldEditor/FieldEditor.stories.tsx +4 -6
  46. package/src/components/FieldEditor/FieldEditor.tsx +2 -3
  47. package/src/components/Form/Form.stories.tsx +8 -7
  48. package/src/components/Form/Form.tsx +82 -55
  49. package/src/components/Form/FormField.tsx +12 -4
  50. package/src/components/Form/FormFieldComponent.stories.tsx +5 -5
  51. package/src/components/Form/FormFieldComponent.tsx +17 -20
  52. package/src/components/Form/FormFieldSet.tsx +106 -104
  53. package/src/components/Form/fields/ArrayField.tsx +6 -8
  54. package/src/components/Form/fields/MarkdownField.tsx +1 -1
  55. package/src/components/Form/fields/RefField.tsx +28 -15
  56. package/src/components/Form/fields/SelectField.tsx +1 -1
  57. package/src/components/Form/fields/SelectOptionField.stories.tsx +2 -2
  58. package/src/components/Form/fields/SelectOptionField.tsx +4 -4
  59. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +9 -13
  60. package/src/components/Settings/Settings.stories.tsx +56 -0
  61. package/src/components/Settings/Settings.tsx +230 -0
  62. package/src/components/{ControlSection → Settings}/index.ts +1 -1
  63. package/src/components/ViewEditor/ViewEditor.stories.tsx +14 -12
  64. package/src/components/ViewEditor/ViewEditor.test.tsx +3 -6
  65. package/src/components/ViewEditor/ViewEditor.tsx +92 -63
  66. package/src/components/index.ts +1 -1
  67. package/src/components/testing.tsx +9 -6
  68. package/src/hooks/useFormHandler.ts +42 -24
  69. package/src/util/index.ts +0 -1
  70. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  71. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  72. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  73. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  74. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  75. package/dist/types/src/components/Form/Form-old.stories.d.ts +0 -131
  76. package/dist/types/src/components/Form/Form-old.stories.d.ts.map +0 -1
  77. package/dist/types/src/util/echo.d.ts +0 -6
  78. package/dist/types/src/util/echo.d.ts.map +0 -1
  79. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  80. package/src/components/ControlSection/ControlSection.tsx +0 -153
  81. package/src/components/Form/Form-old.stories.tsx +0 -314
  82. package/src/util/echo.ts +0 -25
@@ -2,13 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { forwardRef, useMemo } from 'react';
5
+ import React, { useMemo } from 'react';
6
6
 
7
7
  import { type AnyProperties } from '@dxos/echo/internal';
8
8
  import { type SchemaProperty } from '@dxos/effect';
9
- import { type ThemedClassName } from '@dxos/react-ui';
10
- import { mx } from '@dxos/ui-theme';
11
- import { isTruthy } from '@dxos/util';
12
9
 
13
10
  import { type FormHandlerProps } from '../../hooks';
14
11
  import { getFormProperties } from '../../util';
@@ -17,109 +14,114 @@ import { useFormValues } from './Form';
17
14
  import { FormField, type FormFieldProps } from './FormField';
18
15
  import { FormFieldErrorBoundary, FormFieldLabel } from './FormFieldComponent';
19
16
 
20
- export type FormFieldSetProps<T extends AnyProperties> = ThemedClassName<
21
- {
22
- label?: string;
23
- exclude?: (props: SchemaProperty[]) => SchemaProperty[];
24
- sort?: string[];
25
- } & Pick<FormHandlerProps<T>, 'schema'> &
26
- Pick<
27
- FormFieldProps,
28
- | 'path'
29
- | 'autoFocus'
30
- | 'readonly'
31
- | 'layout'
32
- | 'projection'
33
- | 'fieldMap'
34
- | 'fieldProvider'
35
- | 'createOptionLabel'
36
- | 'createOptionIcon'
37
- | 'createInitialValuePath'
38
- | 'db'
39
- | 'schemaHook'
40
- | 'getOptions'
41
- | 'onCreate'
42
- >
43
- >;
44
-
45
- export const FormFieldSet = forwardRef<HTMLDivElement, FormFieldSetProps<any>>(
46
- ({ classNames, label, schema, readonly, path, exclude, sort, projection, layout, ...props }, forwardRef) => {
47
- const values = useFormValues(FormFieldSet.displayName!, path);
48
-
49
- // TODO(burdon): Updates on every value change.
50
- // Remove values dep if can remove from getSchemaProperties.
51
- const properties = useMemo(() => {
52
- if (!schema) {
53
- return [];
54
- }
17
+ const FORM_FIELDSET_NAME = 'Form.FieldSet';
18
+
19
+ export type FormFieldSetProps<T extends AnyProperties> = {
20
+ label?: string;
21
+ sort?: string[];
22
+ exclude?: (props: SchemaProperty[]) => SchemaProperty[];
23
+ } & Pick<FormHandlerProps<T>, 'schema'> &
24
+ Pick<
25
+ FormFieldProps,
26
+ | 'path'
27
+ | 'autoFocus'
28
+ | 'readonly'
29
+ | 'layout'
30
+ | 'projection'
31
+ | 'fieldMap'
32
+ | 'fieldProvider'
33
+ | 'createOptionLabel'
34
+ | 'createOptionIcon'
35
+ | 'createInitialValuePath'
36
+ | 'db'
37
+ | 'schemaHook'
38
+ | 'getOptions'
39
+ | 'onCreate'
40
+ >;
41
+
42
+ /**
43
+ * Renders a set of form fields derived from a schema object.
44
+ */
45
+ export const FormFieldSet = ({
46
+ label,
47
+ schema,
48
+ readonly,
49
+ path,
50
+ sort,
51
+ exclude,
52
+ projection,
53
+ layout,
54
+ ...props
55
+ }: FormFieldSetProps<any>) => {
56
+ const values = useFormValues(FORM_FIELDSET_NAME, path);
57
+
58
+ // TODO(burdon): Updates on every value change.
59
+ // Remove values dep if can remove from getSchemaProperties.
60
+ const properties = useMemo(() => {
61
+ if (!schema) {
62
+ return [];
63
+ }
55
64
 
56
- // TODO(wittjosiah): Reconcile FormInputAnnotation with projection hidden properties & exclude function.
57
- const props = getFormProperties(schema.ast);
58
-
59
- // Use projection-based field management when view and projection are available.
60
- if (projection) {
61
- const fieldProjections = projection.getFieldProjections();
62
- const hiddenProperties = new Set(projection.getHiddenProperties());
63
-
64
- // Filter properties to only include visible ones and order by projection.
65
- const visibleProps = props.filter((prop) => !hiddenProperties.has(prop.name.toString()));
66
- const orderedProps: SchemaProperty[] = [];
67
-
68
- // Add properties in projection field order.
69
- for (const fieldProjection of fieldProjections) {
70
- const fieldPath = String(fieldProjection.field.path);
71
- const prop = visibleProps.find((p) => p.name === fieldPath);
72
- if (prop) {
73
- orderedProps.push(prop);
74
- }
65
+ // TODO(wittjosiah): Reconcile FormInputAnnotation with projection hidden properties & exclude function.
66
+ const schemaProps = getFormProperties(schema.ast);
67
+ const filteredProps = exclude ? exclude(schemaProps) : schemaProps;
68
+
69
+ // Use projection-based field management when view and projection are available.
70
+ if (projection) {
71
+ const fieldProjections = projection.getFieldProjections();
72
+ const hiddenProperties = new Set(projection.getHiddenProperties());
73
+
74
+ // Filter properties to only include visible ones and order by projection.
75
+ const visibleProps = filteredProps.filter((prop) => !hiddenProperties.has(prop.name.toString()));
76
+ const orderedProps: SchemaProperty[] = [];
77
+
78
+ // Add properties in projection field order.
79
+ for (const fieldProjection of fieldProjections) {
80
+ const fieldPath = String(fieldProjection.field.path);
81
+ const prop = visibleProps.find((p) => p.name === fieldPath);
82
+ if (prop) {
83
+ orderedProps.push(prop);
75
84
  }
76
-
77
- // Add any remaining properties not in projection.
78
- const projectionPaths = new Set(fieldProjections.map((projection) => String(projection.field.path)));
79
- const remainingProps = visibleProps.filter((prop) => !projectionPaths.has(prop.name.toString()));
80
- orderedProps.push(...remainingProps);
81
- return orderedProps;
82
85
  }
83
86
 
84
- // Fallback to legacy filter/sort behavior.
85
- const filtered = exclude ? exclude(props) : props;
86
- return sort
87
- ? filtered.sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString()))
88
- : filtered;
89
- }, [schema, values, exclude, sort, projection?.fields]);
90
-
91
- if ((readonly || layout === 'static') && values == null) {
92
- return null;
87
+ // Add any remaining properties not in projection.
88
+ const projectionPaths = new Set(fieldProjections.map((projection) => String(projection.field.path)));
89
+ const remainingProps = visibleProps.filter((prop) => !projectionPaths.has(prop.name.toString()));
90
+ orderedProps.push(...remainingProps);
91
+ return orderedProps;
93
92
  }
94
93
 
95
- return (
96
- <div
97
- role='form'
98
- className={mx('is-full', layout === 'inline' && 'flex flex-col gap-2', classNames)}
99
- ref={forwardRef}
100
- >
101
- {layout !== 'inline' && label && <FormFieldLabel label={label} asChild />}
102
- {properties
103
- .map((property) => {
104
- const name = property.name.toString();
105
- return (
106
- <FormFieldErrorBoundary key={name} path={[...(path ?? []), name]}>
107
- <FormField
108
- type={property.type}
109
- name={name}
110
- path={[...(path ?? []), name]}
111
- readonly={readonly}
112
- layout={layout}
113
- projection={projection}
114
- {...props}
115
- />
116
- </FormFieldErrorBoundary>
117
- );
118
- })
119
- .filter(isTruthy)}
120
- </div>
121
- );
122
- },
123
- );
124
-
125
- FormFieldSet.displayName = 'Form.Fields';
94
+ // Fallback to legacy filter/sort behavior.
95
+ return sort
96
+ ? [...filteredProps].sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString()))
97
+ : filteredProps;
98
+ }, [schema, values, exclude, sort, projection]);
99
+
100
+ if ((readonly || layout === 'static') && values == null) {
101
+ return null;
102
+ }
103
+
104
+ return (
105
+ <>
106
+ {layout !== 'inline' && label && <FormFieldLabel label={label} asChild />}
107
+ {properties.map((property) => {
108
+ const name = property.name.toString();
109
+ return (
110
+ <FormFieldErrorBoundary key={name} path={[...(path ?? []), name]}>
111
+ <FormField
112
+ type={property.type}
113
+ name={name}
114
+ path={[...(path ?? []), name]}
115
+ readonly={readonly}
116
+ layout={layout}
117
+ projection={projection}
118
+ {...props}
119
+ />
120
+ </FormFieldErrorBoundary>
121
+ );
122
+ })}
123
+ </>
124
+ );
125
+ };
126
+
127
+ FormFieldSet.displayName = FORM_FIELDSET_NAME;
@@ -91,9 +91,9 @@ export const ArrayField = ({
91
91
  />
92
92
 
93
93
  {!readonly && layout !== 'static' && (
94
- <div role='none' className='flex flex-col bs-full justify-end'>
94
+ <div role='none' className='flex flex-col h-full justify-end'>
95
95
  {/* NOTE: Aligns with center of last field if multi-field object. */}
96
- <div role='none' className='flex items-center bs-[var(--line-height)]'>
96
+ <div role='none' className='flex items-center h-[2rem]'>
97
97
  <IconButton
98
98
  icon='ph--x--regular'
99
99
  iconOnly
@@ -111,12 +111,7 @@ export const ArrayField = ({
111
111
 
112
112
  {/* TODO(burdon): Get label from schema. */}
113
113
  {!readonly && layout !== 'static' && (
114
- <IconButton
115
- classNames='flex is-full _mlb-cardSpacingBlock'
116
- icon='ph--plus--regular'
117
- label={t('add field')}
118
- onClick={handleAdd}
119
- />
114
+ <IconButton classNames='flex w-full' icon='ph--plus--regular' label={t('add field')} onClick={handleAdd} />
120
115
  )}
121
116
  </>
122
117
  );
@@ -140,6 +135,9 @@ export const getDefaultValue = (ast?: SchemaAST.AST): any => {
140
135
  case 'BooleanKeyword': {
141
136
  return false;
142
137
  }
138
+ case 'Suspend': {
139
+ return getDefaultValue(ast.f());
140
+ }
143
141
  default: {
144
142
  if (ast && isNestedType(ast)) {
145
143
  return {};
@@ -65,7 +65,7 @@ export const MarkdownField = ({
65
65
  disabled={!!readonly}
66
66
  placeholder={placeholder}
67
67
  value={value ?? ''}
68
- classNames={'min-bs-auto max-bs-40 overflow-auto'}
68
+ classNames={'min-h-auto max-h-40 overflow-auto'}
69
69
  onChange={handleChange}
70
70
  onBlur={onBlur}
71
71
  style={{ resize: 'none' }}
@@ -6,14 +6,15 @@ import '@dxos/lit-ui/dx-tag-picker.pcss';
6
6
 
7
7
  import React, { useCallback, useMemo } from 'react';
8
8
 
9
- import { type Database, Entity, Filter, Ref, type Type } from '@dxos/echo';
9
+ import { type Database, Entity, Filter, Obj, Ref, Type } from '@dxos/echo';
10
10
  import { ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/internal';
11
11
  import { useQuery, useSchema as useSchema$ } from '@dxos/echo-react';
12
12
  import { findAnnotation } from '@dxos/effect';
13
13
  import { DXN } from '@dxos/keys';
14
14
  import { DxAnchor } from '@dxos/lit-ui/react';
15
15
  import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
16
- import { descriptionText, mx } from '@dxos/ui-theme';
16
+ import { ParentLabelAnnotationId } from '@dxos/schema';
17
+ import { mx } from '@dxos/ui-theme';
17
18
 
18
19
  import { translationKey } from '../../../translations';
19
20
  import { ObjectPicker, type ObjectPickerContentProps, type RefOption } from '../../ObjectPicker';
@@ -25,23 +26,32 @@ const isRefSnapshot = (val: any): val is { '/': string } => {
25
26
  return typeof val === 'object' && typeof (val as any)?.['/'] === 'string';
26
27
  };
27
28
 
28
- const defaultGetOptions: NonNullable<RefFieldProps['getOptions']> = (results) =>
29
+ const defaultGetOptions: NonNullable<RefFieldProps['getOptions']> = (results, { parentLabel } = {}) =>
29
30
  results.map((result) => {
30
31
  const id = Entity.getDXN(result).toString();
31
- const label = Entity.getLabel(result);
32
+ const parent = parentLabel ? Obj.getParent(result as Obj.Unknown) : undefined;
33
+ const label = parent ? Entity.getLabel(parent) : Entity.getLabel(result);
32
34
  return { id, label: label ?? id };
33
35
  });
34
36
 
35
37
  const defaultResultsHook: NonNullable<RefFieldProps['resultsHook']> = (db, typename) =>
36
- useQuery(db, typename ? Filter.typename(typename) : Filter.nothing());
38
+ useQuery(
39
+ db,
40
+ typename
41
+ ? // For Ref.Ref(Obj.Unknown) we want to show all objects.
42
+ typename === Type.getTypename(Obj.Unknown)
43
+ ? Filter.everything()
44
+ : Filter.typename(typename)
45
+ : Filter.nothing(),
46
+ );
37
47
 
38
48
  export type RefFieldProps = FormFieldComponentProps &
39
49
  Pick<ObjectPickerContentProps, 'createOptionLabel' | 'createOptionIcon' | 'createInitialValuePath'> & {
40
50
  db?: Database.Database;
41
51
  resultsHook?: (db?: Database.Database, typename?: string) => Entity.Any[];
42
- schemaHook?: (db?: Database.Database, typename?: string) => Type.Entity.Any;
43
- getOptions?: (objects: Entity.Any[]) => RefOption[];
44
- onCreate?: (schema: Type.Entity.Any, values: any) => void;
52
+ schemaHook?: (db?: Database.Database, typename?: string) => Type.AnyEntity;
53
+ getOptions?: (objects: Entity.Any[], options?: { parentLabel?: boolean }) => RefOption[];
54
+ onCreate?: (schema: Type.AnyEntity, values: any) => void;
45
55
  };
46
56
 
47
57
  export const RefField = (props: RefFieldProps) => {
@@ -72,7 +82,10 @@ export const RefField = (props: RefFieldProps) => {
72
82
  );
73
83
 
74
84
  const results = useResults(db, typename);
75
- const options = useMemo(() => getOptions(results), [results, getOptions]);
85
+ const options = useMemo(() => {
86
+ const parentLabel = type ? findAnnotation<boolean>(type, ParentLabelAnnotationId) === true : false;
87
+ return getOptions(results, { parentLabel });
88
+ }, [results, getOptions, type]);
76
89
 
77
90
  const handleGetValue = useCallback(() => {
78
91
  const formValue = getValue();
@@ -135,9 +148,9 @@ export const RefField = (props: RefFieldProps) => {
135
148
  <div>
136
149
  {readonly ? (
137
150
  !item ? (
138
- <p className={mx(descriptionText, 'mbe-2')}>{t('empty readonly ref field label')}</p>
151
+ <p className={mx('text-description', 'mb-2')}>{t('empty readonly ref field label')}</p>
139
152
  ) : (
140
- <DxAnchor key={item.id} refid={item.id} rootclassname='mie-1'>
153
+ <DxAnchor key={item.id} dxn={item.id} rootclassname='me-1'>
141
154
  {item.label}
142
155
  </DxAnchor>
143
156
  )
@@ -145,13 +158,13 @@ export const RefField = (props: RefFieldProps) => {
145
158
  <ObjectPicker.Root>
146
159
  <ObjectPicker.Trigger asChild classNames='p-0'>
147
160
  {item ? (
148
- <div className='flex gap-2 is-full'>
161
+ <div className='flex gap-2 w-full'>
149
162
  <Input.Root key={item.id}>
150
- <Input.TextInput value={item.label} readOnly classNames='is-full' />
163
+ <Input.TextInput value={item.label} readOnly classNames='w-full' />
151
164
  </Input.Root>
152
165
  </div>
153
166
  ) : (
154
- <Button classNames='is-full text-start gap-2'>
167
+ <Button classNames='w-full text-start gap-2'>
155
168
  <div role='none' className='grow overflow-hidden'>
156
169
  <span className='flex truncate text-description'>{placeholder ?? t('ref field placeholder')}</span>
157
170
  </div>
@@ -161,7 +174,7 @@ export const RefField = (props: RefFieldProps) => {
161
174
  </ObjectPicker.Trigger>
162
175
  <ObjectPicker.Portal>
163
176
  <ObjectPicker.Content
164
- classNames='popover-card-width'
177
+ classNames='dx-card-popover-width'
165
178
  options={options}
166
179
  selectedIds={selectedIds}
167
180
  createSchema={createSchema && omitId(createSchema)}
@@ -43,7 +43,7 @@ export const SelectField = ({
43
43
  ) : (
44
44
  <Select.Root value={value} onValueChange={handleValueChange}>
45
45
  {/* TODO(burdon): Placeholder not working? */}
46
- <Select.TriggerButton classNames='is-full' disabled={!!readonly} placeholder={placeholder} />
46
+ <Select.TriggerButton classNames='w-full' disabled={!!readonly} placeholder={placeholder} />
47
47
  <Select.Portal>
48
48
  <Select.Content>
49
49
  <Select.Viewport>
@@ -7,7 +7,7 @@ import * as Schema from 'effect/Schema';
7
7
  import React, { useState } from 'react';
8
8
 
9
9
  import { SelectOption } from '@dxos/echo/internal';
10
- import { withTheme } from '@dxos/react-ui/testing';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
 
12
12
  import { translations } from '../../../translations';
13
13
  import { TestLayout } from '../../testing';
@@ -50,7 +50,7 @@ const meta = {
50
50
  title: 'ui/react-ui-form/SelectOptionField',
51
51
  component: SelectOptionField as any,
52
52
  render: DefaultStory,
53
- decorators: [withTheme],
53
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
54
54
  parameters: {
55
55
  layout: 'fullscreen',
56
56
  translations,
@@ -128,13 +128,13 @@ export const SelectOptionField = ({
128
128
  readonly={!!readonly}
129
129
  >
130
130
  {({ items }) => (
131
- <div role='list' className='is-full overflow-auto'>
131
+ <div role='list' className='w-full overflow-auto'>
132
132
  {items?.map((item) => (
133
133
  <div key={item.id}>
134
134
  <List.Item
135
135
  role='button'
136
136
  item={item}
137
- classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-sm']}
137
+ classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-xs']}
138
138
  aria-expanded={selected === item.id}
139
139
  >
140
140
  <div className='flex items-center'>
@@ -152,7 +152,7 @@ export const SelectOptionField = ({
152
152
  />
153
153
  </div>
154
154
  {selected === item.id && (
155
- <div className='pli-cardSpacingInline plb-cardSpacingBlock flex flex-col gap-1 density-fine'>
155
+ <div className='flex flex-col p-form-padding gap-1 dx-density-fine'>
156
156
  {/* 16px to match drag handle width. */}
157
157
  <Input.Label classNames='text-xs'>{t('select option label')}</Input.Label>
158
158
  <div className='grid grid-cols-[1fr_min-content_min-content] gap-1'>
@@ -185,7 +185,7 @@ export const SelectOptionField = ({
185
185
  label={t('select option add')}
186
186
  onClick={handleAdd}
187
187
  disabled={!!readonly}
188
- classNames='is-full'
188
+ classNames='w-full'
189
189
  />
190
190
  </div>
191
191
  )}
@@ -74,15 +74,15 @@ const DefaultStory = () => {
74
74
  );
75
75
 
76
76
  return (
77
- <div role='none' className='flex is-96'>
77
+ <div role='none' className='flex w-96'>
78
78
  <ObjectPicker.Root open={isOpen} onOpenChange={setIsOpen}>
79
79
  <ObjectPicker.Trigger asChild>
80
- <Button variant='primary' data-testid='trigger' classNames='is-full'>
80
+ <Button variant='primary' data-testid='trigger' classNames='w-full'>
81
81
  Select Person
82
82
  </Button>
83
83
  </ObjectPicker.Trigger>
84
84
  <ObjectPicker.Content
85
- classNames='popover-card-width'
85
+ classNames='dx-card-popover-width'
86
86
  options={options}
87
87
  createSchema={personSchema}
88
88
  createOptionLabel={['create new person label', { ns: osTranslations }]}
@@ -100,7 +100,7 @@ const meta = {
100
100
  title: 'ui/react-ui-form/ObjectPicker',
101
101
  render: DefaultStory,
102
102
  decorators: [
103
- withTheme,
103
+ withTheme(),
104
104
  withClientProvider({
105
105
  types: [Person.Person],
106
106
  createIdentity: true,
@@ -201,14 +201,10 @@ export const WithTest: Story = {
201
201
  await userEvent.click(saveButton);
202
202
 
203
203
  // Check that clicking the save button calls onCreate with expected values.
204
- await expect(mockHandleCreate).toHaveBeenCalledWith({
205
- fullName: unrelatedTerm,
206
- addresses: [],
207
- phoneNumbers: [],
208
- emails: [],
209
- identities: [],
210
- urls: [],
211
- fields: [],
212
- });
204
+ await expect(mockHandleCreate).toHaveBeenCalledWith(
205
+ expect.objectContaining({
206
+ fullName: unrelatedTerm,
207
+ }),
208
+ );
213
209
  },
214
210
  };
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { faker } from '@dxos/random';
9
+ import { Button, Input } from '@dxos/react-ui';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+
12
+ import { translations } from '../../translations';
13
+
14
+ import { Settings } from './Settings';
15
+
16
+ faker.seed(132);
17
+
18
+ const DefaultStory = () => {
19
+ return (
20
+ <Settings.Root>
21
+ <Settings.Section title='Settings' description={faker.lorem.paragraphs(1)}>
22
+ <Settings.Group>
23
+ <Settings.ItemInput title={faker.lorem.sentence(2)} description={faker.lorem.paragraphs(1)}>
24
+ <Input.Root>
25
+ <Input.TextInput placeholder='Input' />
26
+ </Input.Root>
27
+ </Settings.ItemInput>
28
+ <Settings.ItemInput title={faker.lorem.sentence(2)} description={faker.lorem.paragraphs(2)}>
29
+ <Input.Root>
30
+ <Input.Switch />
31
+ </Input.Root>
32
+ </Settings.ItemInput>
33
+ <Settings.ItemInput title={faker.lorem.sentence(3)} description={faker.lorem.paragraphs(2)}>
34
+ <Button>Test</Button>
35
+ </Settings.ItemInput>
36
+ </Settings.Group>
37
+ </Settings.Section>
38
+ </Settings.Root>
39
+ );
40
+ };
41
+
42
+ const meta = {
43
+ title: 'ui/react-ui-form/Settings',
44
+ render: DefaultStory,
45
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
46
+ parameters: {
47
+ layout: 'fullscreen',
48
+ translations,
49
+ },
50
+ } satisfies Meta;
51
+
52
+ export default meta;
53
+
54
+ type Story = StoryObj<typeof meta>;
55
+
56
+ export const Default: Story = {};