@dxos/react-ui-form 0.8.3-staging.0fa589b → 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.
Files changed (53) hide show
  1. package/dist/lib/browser/index.mjs +69 -75
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +69 -75
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +1 -1
  8. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -4
  10. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +3 -3
  12. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.stories.d.ts +1 -1
  14. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +1 -1
  16. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
  18. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  19. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +4 -4
  20. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/deprecated/Form.d.ts +3 -3
  22. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  23. package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -1
  24. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  25. package/dist/types/src/index.d.ts +1 -1
  26. package/dist/types/src/index.d.ts.map +1 -1
  27. package/dist/types/src/translations.d.ts +56 -57
  28. package/dist/types/src/translations.d.ts.map +1 -1
  29. package/dist/types/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +30 -32
  31. package/src/components/ControlSection/ControlSection.stories.tsx +2 -2
  32. package/src/components/FieldEditor/FieldEditor.stories.tsx +14 -12
  33. package/src/components/FieldEditor/FieldEditor.test.tsx +11 -11
  34. package/src/components/FieldEditor/FieldEditor.tsx +5 -15
  35. package/src/components/Form/ArrayField.tsx +1 -1
  36. package/src/components/Form/Form.stories.tsx +2 -2
  37. package/src/components/Form/RefField.tsx +1 -1
  38. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +2 -2
  39. package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
  40. package/src/components/ViewEditor/ViewEditor.stories.tsx +16 -10
  41. package/src/components/ViewEditor/ViewEditor.test.tsx +37 -35
  42. package/src/components/ViewEditor/ViewEditor.tsx +17 -32
  43. package/src/components/deprecated/Form.stories.tsx +5 -5
  44. package/src/components/deprecated/Form.tsx +4 -4
  45. package/src/components/testing.tsx +1 -1
  46. package/src/index.ts +1 -1
  47. package/src/translations.ts +4 -2
  48. package/dist/lib/node/index.cjs +0 -2416
  49. package/dist/lib/node/index.cjs.map +0 -7
  50. package/dist/lib/node/meta.json +0 -1
  51. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +0 -10
  52. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +0 -1
  53. 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-staging.0fa589b",
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/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"
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.14.21",
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.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"
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
- "@phosphor-icons/react": "^2.1.5",
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-theme": "0.8.3-staging.0fa589b",
75
- "@dxos/react-ui": "0.8.3-staging.0fa589b"
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 { ViewProjection } from '@dxos/schema';
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: ViewProjection;
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] = { props, projection } satisfies FieldEditorDebugObjects;
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} projection={projection} onSave={handleComplete} />
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
- view: testView,
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 { ViewProjection } from '@dxos/schema';
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.schema.properties!.name;
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 ViewType,
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
- view: ViewType;
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 && view.fields.find((f) => f.path === property && f.path !== field.path)) {
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
- [view.fields, field],
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-1 flex'
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='rounded-sm bg-inputSurface p-1.5'
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={mx('p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay')}
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 { ViewProjection, ViewType, createView } from '@dxos/schema';
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: ViewType;
26
- projection: ViewProjection;
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<ViewType>();
35
- const [projection, setProjection] = useState<ViewProjection>();
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({ name: 'Test', typename: schema.typename, jsonSchema: toJsonSchema(TestSchema) });
46
- const projection = new ViewProjection(schema.jsonSchema, view);
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(ViewType));
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 { ViewProjection, ViewType } from '@dxos/schema';
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).toBeInstanceOf(ViewType);
23
- expect(debugObjects.projection).toBeInstanceOf(ViewProjection);
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('View')).toBeInTheDocument();
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((field: any) => field.path === 'added_property');
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
  });