@dxos/react-ui-form 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d

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 (70) hide show
  1. package/dist/lib/browser/index.mjs +223 -205
  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 +223 -205
  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 +5 -5
  8. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +5 -5
  9. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  10. package/dist/types/src/components/Form/Form.d.ts +5 -4
  11. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Form.stories.d.ts +47 -33
  13. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormContext.d.ts +3 -3
  17. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Input.d.ts +2 -2
  19. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/RefField.d.ts +1 -0
  21. package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +5 -5
  23. package/dist/types/src/components/Form/factory.d.ts +2 -2
  24. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  25. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +1 -1
  26. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  27. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +6 -5
  28. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +11 -9
  30. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  31. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +8 -8
  32. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  33. package/dist/types/src/hooks/useForm.d.ts +4 -4
  34. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  35. package/dist/types/src/hooks/useQueryRefOptions.d.ts +1 -0
  36. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
  37. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
  38. package/dist/types/src/translations.d.ts +5 -5
  39. package/dist/types/src/util/field.d.ts +3 -3
  40. package/dist/types/src/util/field.d.ts.map +1 -1
  41. package/dist/types/src/util/formatting.d.ts +2 -2
  42. package/dist/types/src/util/formatting.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +32 -31
  45. package/src/components/ControlSection/ControlSection.stories.tsx +1 -1
  46. package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
  47. package/src/components/FieldEditor/FieldEditor.tsx +3 -3
  48. package/src/components/Form/ArrayField.tsx +3 -1
  49. package/src/components/Form/Defaults.tsx +1 -1
  50. package/src/components/Form/Form.stories.tsx +24 -24
  51. package/src/components/Form/Form.tsx +8 -5
  52. package/src/components/Form/FormActions.tsx +7 -2
  53. package/src/components/Form/FormContent.tsx +53 -13
  54. package/src/components/Form/FormContext.tsx +4 -4
  55. package/src/components/Form/Input.tsx +2 -2
  56. package/src/components/Form/RefField.tsx +16 -2
  57. package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
  58. package/src/components/Form/factory.tsx +7 -4
  59. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +25 -25
  60. package/src/components/ObjectPicker/ObjectPicker.tsx +14 -14
  61. package/src/components/ViewEditor/ViewEditor.stories.tsx +46 -32
  62. package/src/components/ViewEditor/ViewEditor.test.tsx +4 -3
  63. package/src/components/ViewEditor/ViewEditor.tsx +41 -97
  64. package/src/hooks/useForm.ts +8 -7
  65. package/src/hooks/useQueryRefOptions.ts +6 -3
  66. package/src/hooks/useRefQueryLookupHandler.ts +5 -5
  67. package/src/translations.ts +5 -5
  68. package/src/util/field.ts +25 -25
  69. package/src/util/formatting.ts +22 -22
  70. package/src/util/refs.ts +6 -6
@@ -9,12 +9,11 @@ import { expect, fn, userEvent, within } from 'storybook/test';
9
9
 
10
10
  import { Obj } from '@dxos/echo';
11
11
  import { faker } from '@dxos/random';
12
- import { useClient } from '@dxos/react-client';
13
12
  import { Filter, useQuery } from '@dxos/react-client/echo';
14
13
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
15
14
  import { Button } from '@dxos/react-ui';
16
15
  import { withTheme } from '@dxos/react-ui/testing';
17
- import { DataType } from '@dxos/schema';
16
+ import { Person } from '@dxos/types';
18
17
 
19
18
  import { translations } from '../../translations';
20
19
 
@@ -23,19 +22,18 @@ import { ObjectPicker } from './ObjectPicker';
23
22
  faker.seed(1);
24
23
 
25
24
  const createPerson = () =>
26
- Obj.make(DataType.Person, {
25
+ Obj.make(Person.Person, {
27
26
  fullName: faker.person.fullName(),
28
27
  });
29
28
 
30
29
  const omitId = Schema.omit<any, any, ['id']>('id');
31
- const personSchema = omitId(DataType.Person);
30
+ const personSchema = omitId(Person.Person);
32
31
 
33
32
  // Mock functions for testing
34
33
  const mockHandleSelect = fn();
35
34
  const mockHandleCreate = fn();
36
35
 
37
36
  const DefaultStory = () => {
38
- const client = useClient();
39
37
  const { space } = useClientProvider();
40
38
  const [isOpen, setIsOpen] = useState(false);
41
39
 
@@ -67,7 +65,7 @@ const DefaultStory = () => {
67
65
  (values: any) => {
68
66
  console.log('[on create]', values);
69
67
  if (!space) return;
70
- const newPerson = space.db.add(Obj.make(DataType.Person, values));
68
+ const newPerson = space.db.add(Obj.make(Person.Person, values));
71
69
  mockHandleCreate(values);
72
70
  return newPerson;
73
71
  },
@@ -103,7 +101,7 @@ const meta = {
103
101
  decorators: [
104
102
  withTheme,
105
103
  withClientProvider({
106
- types: [DataType.Person],
104
+ types: [Person.Person],
107
105
  createIdentity: true,
108
106
  createSpace: true,
109
107
  }),
@@ -119,37 +117,39 @@ export default meta;
119
117
 
120
118
  type Story = StoryObj<typeof meta>;
121
119
 
122
- export const Default: Story = {
120
+ export const Default: Story = {};
121
+
122
+ export const WithTest: Story = {
123
123
  play: async ({ canvasElement }) => {
124
124
  const canvas = within(canvasElement);
125
125
  const body = within(document.body);
126
126
 
127
- // Find and click the trigger button
127
+ // Find and click the trigger button.
128
128
  const triggerButton = await canvas.findByTestId('trigger', undefined, { timeout: 5000 });
129
129
  await expect(triggerButton).toBeVisible();
130
130
  await userEvent.click(triggerButton);
131
131
 
132
- // Check that the listbox renders when the trigger is clicked
132
+ // Check that the listbox renders when the trigger is clicked.
133
133
  const listbox = await body.findByRole('listbox', undefined, { timeout: 5000 });
134
134
  await expect(listbox).toBeVisible();
135
135
 
136
- // Find the search input
136
+ // Find the search input.
137
137
  const searchInput = await body.findByPlaceholderText('Search…');
138
138
  await expect(searchInput).toBeVisible();
139
139
 
140
- // Test searching filters the results - get initial options count
140
+ // Test searching filters the results - get initial options count.
141
141
  const initialOptions = await body.findAllByRole('option');
142
142
  const initialCount = initialOptions.length;
143
143
  await expect(initialCount).toBeGreaterThan(0);
144
144
 
145
- // Type a search term that should filter results
145
+ // Type a search term that should filter results.
146
146
  await userEvent.type(searchInput, 'John');
147
147
 
148
- // Wait a moment for filtering
148
+ // Wait a moment for filtering.
149
149
  await new Promise((resolve) => setTimeout(resolve, 100));
150
150
 
151
151
  // Check that results are filtered (should be fewer options)
152
- const filteredOptions = await body.findAllByRole('option');
152
+ const _filteredOptions = await body.findAllByRole('option');
153
153
  // Note: We expect either fewer results or the same if no John exists
154
154
 
155
155
  // Clear search and test selecting a result
@@ -160,46 +160,46 @@ export const Default: Story = {
160
160
  const firstOption = updatedOptions[0];
161
161
  await expect(firstOption).toBeVisible();
162
162
 
163
- const firstOptionText = await firstOption.textContent;
163
+ const _firstOptionText = firstOption.textContent;
164
164
  await userEvent.click(firstOption);
165
165
 
166
166
  // Check that clicking a result fires onSelect with that id
167
167
  // The onSelect should have been called with the selected option's id
168
168
  await expect(mockHandleSelect).toHaveBeenCalled();
169
169
 
170
- // Re-open the picker to test create functionality
170
+ // Re-open the picker to test create functionality.
171
171
  await userEvent.click(triggerButton);
172
172
 
173
- // Wait for listbox to appear again
173
+ // Wait for listbox to appear again.
174
174
  const newListbox = await body.findByRole('listbox', undefined, { timeout: 5000 });
175
175
  await expect(newListbox).toBeVisible();
176
176
 
177
177
  const newSearchInput = await body.findByPlaceholderText('Search…');
178
178
 
179
- // Test searching an unrelated term provides the __create__ option
179
+ // Test searching an unrelated term provides the __create__ option.
180
180
  const unrelatedTerm = 'ZxyUnrelatedName123';
181
181
  await userEvent.type(newSearchInput, unrelatedTerm);
182
182
 
183
- // Wait for the create option to appear
183
+ // Wait for the create option to appear.
184
184
  await new Promise((resolve) => setTimeout(resolve, 200));
185
185
 
186
- // Look for the create option
186
+ // Look for the create option.
187
187
  const createOption = await body.findByRole('option', undefined, { timeout: 3000 });
188
188
  await expect(createOption).toBeVisible();
189
189
 
190
- // Click the create option - this should change state to the form field
190
+ // Click the create option - this should change state to the form field.
191
191
  await userEvent.click(createOption);
192
192
 
193
- // Check that clicking that option changes state to the form field
193
+ // Check that clicking that option changes state to the form field.
194
194
  const form = await body.findByTestId('create-referenced-object-form', undefined, { timeout: 5000 });
195
195
  await expect(form).toBeVisible();
196
196
 
197
- // Find and click the save button
197
+ // Find and click the save button.
198
198
  const saveButton = await within(form).findByTestId('save-button');
199
199
  await expect(saveButton).toBeVisible();
200
200
  await userEvent.click(saveButton);
201
201
 
202
- // Check that clicking the save button calls onCreate with expected values
202
+ // Check that clicking the save button calls onCreate with expected values.
203
203
  await expect(mockHandleCreate).toHaveBeenCalledWith({
204
204
  fullName: unrelatedTerm,
205
205
  });
@@ -6,7 +6,7 @@ import type * as Schema from 'effect/Schema';
6
6
  import React, { type KeyboardEvent, type MouseEvent, forwardRef, useCallback, useMemo, useState } from 'react';
7
7
 
8
8
  import { Icon, Popover, type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
- import { PopoverCombobox } from '@dxos/react-ui-searchlist';
9
+ import { Combobox } from '@dxos/react-ui-searchlist';
10
10
 
11
11
  import { type QueryTag } from '../../hooks';
12
12
  import { translationKey } from '../../translations';
@@ -99,7 +99,7 @@ const ObjectPickerContent = forwardRef<HTMLDivElement, ObjectPickerContentProps>
99
99
  );
100
100
 
101
101
  return (
102
- <PopoverCombobox.Content
102
+ <Combobox.Content
103
103
  {...props}
104
104
  ref={ref}
105
105
  filter={(value, search) => (value === '__create__' || labelById[value]?.includes(search.toLowerCase()) ? 1 : 0)}
@@ -118,15 +118,15 @@ const ObjectPickerContent = forwardRef<HTMLDivElement, ObjectPickerContentProps>
118
118
  </Popover.Viewport>
119
119
  ) : (
120
120
  <>
121
- <PopoverCombobox.Input
121
+ <Combobox.Input
122
122
  placeholder={t('ref field combobox input placeholder')}
123
123
  value={searchString}
124
124
  onValueChange={setSearchString}
125
125
  autoFocus
126
126
  />
127
- <PopoverCombobox.List>
127
+ <Combobox.List>
128
128
  {options.map((option) => (
129
- <PopoverCombobox.Item
129
+ <Combobox.Item
130
130
  key={option.id}
131
131
  value={option.id}
132
132
  onSelect={() => onSelect(option.id)}
@@ -134,19 +134,19 @@ const ObjectPickerContent = forwardRef<HTMLDivElement, ObjectPickerContentProps>
134
134
  >
135
135
  <span className='grow'>{option.label}</span>
136
136
  {selectedIds?.includes(option.id) && <Icon icon='ph--check--regular' />}
137
- </PopoverCombobox.Item>
137
+ </Combobox.Item>
138
138
  ))}
139
139
  {searchString.length > 0 && createOptionLabel && createOptionIcon && createSchema && onCreate && (
140
- <PopoverCombobox.Item value='__create__' classNames='flex items-center gap-2'>
140
+ <Combobox.Item value='__create__' classNames='flex items-center gap-2'>
141
141
  <Icon icon={createOptionIcon} />
142
142
  {t(createOptionLabel[0], { ns: createOptionLabel[1].ns, text: searchString })}
143
- </PopoverCombobox.Item>
143
+ </Combobox.Item>
144
144
  )}
145
- </PopoverCombobox.List>
145
+ </Combobox.List>
146
146
  </>
147
147
  )}
148
- <PopoverCombobox.Arrow />
149
- </PopoverCombobox.Content>
148
+ <Combobox.Arrow />
149
+ </Combobox.Content>
150
150
  );
151
151
  },
152
152
  );
@@ -154,8 +154,8 @@ const ObjectPickerContent = forwardRef<HTMLDivElement, ObjectPickerContentProps>
154
154
  ObjectPickerContent.displayName = 'ObjectPickerContent';
155
155
 
156
156
  export const ObjectPicker = {
157
- Root: PopoverCombobox.Root,
158
- Trigger: PopoverCombobox.Trigger,
159
- VirtualTrigger: PopoverCombobox.VirtualTrigger,
157
+ Root: Combobox.Root,
158
+ Trigger: Combobox.Trigger,
159
+ VirtualTrigger: Combobox.VirtualTrigger,
160
160
  Content: ObjectPickerContent,
161
161
  };
@@ -6,35 +6,45 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
8
 
9
- import { DXN, Filter, Obj, Query, Type } from '@dxos/echo';
10
- import { type EchoSchema, Format, toJsonSchema } from '@dxos/echo/internal';
11
- import { QueryBuilder } from '@dxos/echo-query';
12
- import { useSpace } from '@dxos/react-client/echo';
13
- import { withClientProvider } from '@dxos/react-client/testing';
9
+ import { DXN, Filter, Obj, Query, type QueryAST, Tag, Type } from '@dxos/echo';
10
+ import { type EchoSchema, Format } from '@dxos/echo/internal';
11
+ import { useQuery } from '@dxos/react-client/echo';
12
+ import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
14
13
  import { useAsyncEffect } from '@dxos/react-ui';
15
14
  import { withTheme } from '@dxos/react-ui/testing';
16
- import { type DataType, type ProjectionModel, createView, getTypenameFromQuery } from '@dxos/schema';
15
+ import { type ProjectionModel, View, getTypenameFromQuery } from '@dxos/schema';
16
+ import { Employer, Organization, Person, Project } from '@dxos/types';
17
17
 
18
18
  import { translations } from '../../translations';
19
19
  import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
20
20
 
21
21
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
22
22
 
23
+ const types = [
24
+ // TODO(burdon): Get label from annotation.
25
+ { value: Organization.Organization.typename, label: 'Organization' },
26
+ { value: Person.Person.typename, label: 'Person' },
27
+ { value: Type.getTypename(Project.Project), label: 'Project' },
28
+ { value: Employer.Employer.typename, label: 'Employer' },
29
+ ];
30
+
23
31
  // Type definition for debug objects exposed to tests.
24
32
  export type ViewEditorDebugObjects = {
25
33
  schema: EchoSchema;
26
- view: DataType.View;
34
+ view: View.View;
27
35
  projection: ProjectionModel;
28
36
  };
29
37
 
30
38
  type StoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
31
39
 
32
40
  const DefaultStory = (props: StoryProps) => {
33
- const space = useSpace();
41
+ const { space } = useClientProvider();
34
42
  const [schema, setSchema] = useState<EchoSchema>();
35
- const [view, setView] = useState<DataType.View>();
43
+ const [view, setView] = useState<View.View>();
36
44
  const projectionRef = useRef<ProjectionModel>(null);
37
45
 
46
+ const tags = useQuery(space, Filter.type(Tag.Tag));
47
+
38
48
  useAsyncEffect(async () => {
39
49
  if (space) {
40
50
  const TestSchema = Schema.Struct({
@@ -60,12 +70,10 @@ const DefaultStory = (props: StoryProps) => {
60
70
  );
61
71
 
62
72
  const [testSchema] = await space.db.schemaRegistry.register([TestSchema, AlternateSchema]);
63
- const view = createView({
73
+ const view = View.make({
64
74
  name: 'Test',
65
75
  query: Query.select(Filter.type(TestSchema)),
66
- queryString: 'Query.select(Filter.type(TestSchema))',
67
- jsonSchema: toJsonSchema(TestSchema),
68
- presentation: Obj.make(Type.Expando, {}),
76
+ jsonSchema: Type.toJsonSchema(TestSchema),
69
77
  });
70
78
 
71
79
  setSchema(testSchema);
@@ -74,38 +82,31 @@ const DefaultStory = (props: StoryProps) => {
74
82
  }, [space]);
75
83
 
76
84
  const updateViewQuery = useCallback(
77
- async (queryString: string, target?: string) => {
85
+ async (newQuery: QueryAST.Query, target?: string) => {
78
86
  if (!schema || !view || !space) {
79
87
  return;
80
88
  }
81
89
 
82
- if (props.mode === 'query') {
83
- view.query.string = queryString;
84
-
85
- const builder = new QueryBuilder();
86
- const filter = builder.build(queryString) ?? Filter.nothing();
90
+ if (props.mode === 'tag') {
87
91
  const queue = target && DXN.tryParse(target) ? target : undefined;
88
- const newQuery = queue ? Query.select(filter).options({ queues: [queue] }) : Query.select(filter);
89
- view.query.ast = newQuery.ast;
92
+ const query = queue ? Query.fromAst(newQuery).options({ queues: [queue] }) : Query.fromAst(newQuery);
93
+ view.query.ast = query.ast;
90
94
 
91
- const typename = getTypenameFromQuery(newQuery.ast);
95
+ const typename = getTypenameFromQuery(query.ast);
92
96
  const [newSchema] = await space.db.schemaRegistry.query({ typename }).run();
93
97
  if (!newSchema) {
94
98
  return;
95
99
  }
96
100
 
97
- const newView = createView({
98
- query: newQuery,
101
+ const newView = View.make({
102
+ query,
99
103
  jsonSchema: newSchema.jsonSchema,
100
- presentation: Obj.make(Type.Expando, {}),
101
104
  });
102
105
  view.projection = Obj.getSnapshot(newView).projection;
103
106
  setSchema(() => newSchema);
104
107
  } else {
105
- const typename = queryString;
106
- const newQuery = Query.select(Filter.typename(typename));
107
- view.query.ast = newQuery.ast;
108
- schema.updateTypename(typename);
108
+ view.query.ast = newQuery;
109
+ schema.updateTypename(getTypenameFromQuery(newQuery));
109
110
  }
110
111
  },
111
112
  [view, schema],
@@ -147,6 +148,8 @@ const DefaultStory = (props: StoryProps) => {
147
148
  registry={space?.db.schemaRegistry}
148
149
  mode={props.mode}
149
150
  readonly={props.readonly}
151
+ types={types}
152
+ tags={tags}
150
153
  onQueryChanged={updateViewQuery}
151
154
  onDelete={handleDelete}
152
155
  />
@@ -158,7 +161,18 @@ const DefaultStory = (props: StoryProps) => {
158
161
  const meta = {
159
162
  title: 'ui/react-ui-form/ViewEditor',
160
163
  render: DefaultStory,
161
- decorators: [withClientProvider({ createSpace: true }), withTheme],
164
+ decorators: [
165
+ withClientProvider({
166
+ createSpace: true,
167
+ types: [Tag.Tag],
168
+ onCreateSpace: ({ space }) => {
169
+ space.db.add(Tag.make({ label: 'Important' }));
170
+ space.db.add(Tag.make({ label: 'Investor' }));
171
+ space.db.add(Tag.make({ label: 'New' }));
172
+ },
173
+ }),
174
+ withTheme,
175
+ ],
162
176
  parameters: {
163
177
  layout: 'fullscreen',
164
178
  translations,
@@ -177,8 +191,8 @@ export const Readonly: Story = {
177
191
  },
178
192
  };
179
193
 
180
- export const QueryMode: Story = {
194
+ export const TagQueryMode: Story = {
181
195
  args: {
182
- mode: 'query',
196
+ mode: 'tag',
183
197
  },
184
198
  };
@@ -7,7 +7,7 @@ import { cleanup, fireEvent, screen, waitFor } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
9
  import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
10
- import { DataType, ProjectionModel } from '@dxos/schema';
10
+ import { ProjectionModel, View } from '@dxos/schema';
11
11
 
12
12
  import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
13
13
 
@@ -20,7 +20,7 @@ const getViewEditorDebugObjects = (): ViewEditorDebugObjects => {
20
20
  const debugObjects = (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] as ViewEditorDebugObjects;
21
21
  expect(debugObjects).toBeDefined();
22
22
  expect(debugObjects.schema).toBeInstanceOf(EchoSchema);
23
- expect(isInstanceOf(DataType.View, debugObjects.view)).toBeTruthy();
23
+ expect(isInstanceOf(View.View, debugObjects.view)).toBeTruthy();
24
24
  expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
25
25
  return debugObjects;
26
26
  };
@@ -134,7 +134,8 @@ describe('ViewEditor', () => {
134
134
  expect(addedPropertyProjection!.props.property).toBe('added_property');
135
135
  });
136
136
 
137
- test('delete property', async () => {
137
+ // TODO(mykola): Fix flaky test.
138
+ test('delete property', { retry: 2 }, async () => {
138
139
  await Default.run();
139
140
  await waitForViewEditor();
140
141