@dxos/plugin-sheet 0.7.1 → 0.7.2-main.f1adc9f

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 (55) hide show
  1. package/dist/lib/browser/{SheetContainer-YAMIOFC6.mjs → SheetContainer-DBDIZU6U.mjs} +4 -3
  2. package/dist/lib/browser/{SheetContainer-YAMIOFC6.mjs.map → SheetContainer-DBDIZU6U.mjs.map} +3 -3
  3. package/dist/lib/browser/{chunk-QHQFM7LV.mjs → chunk-IXA5HC36.mjs} +48 -22
  4. package/dist/lib/browser/{chunk-QHQFM7LV.mjs.map → chunk-IXA5HC36.mjs.map} +4 -4
  5. package/dist/lib/browser/index.mjs +8 -13
  6. package/dist/lib/browser/index.mjs.map +3 -3
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/node/{SheetContainer-BSDHHYXE.cjs → SheetContainer-3373SORI.cjs} +16 -15
  9. package/dist/lib/node/{SheetContainer-BSDHHYXE.cjs.map → SheetContainer-3373SORI.cjs.map} +3 -3
  10. package/dist/lib/node/{chunk-J5ZFWMOD.cjs → chunk-TS6IBEPZ.cjs} +34 -10
  11. package/dist/lib/node/{chunk-J5ZFWMOD.cjs.map → chunk-TS6IBEPZ.cjs.map} +4 -4
  12. package/dist/lib/node/index.cjs +10 -15
  13. package/dist/lib/node/index.cjs.map +3 -3
  14. package/dist/lib/node/meta.json +1 -1
  15. package/dist/lib/node-esm/{SheetContainer-T47T2NYJ.mjs → SheetContainer-CH2RYBXJ.mjs} +4 -3
  16. package/dist/lib/node-esm/{SheetContainer-T47T2NYJ.mjs.map → SheetContainer-CH2RYBXJ.mjs.map} +3 -3
  17. package/dist/lib/node-esm/{chunk-6GSTEN7N.mjs → chunk-7F3BRKP7.mjs} +48 -22
  18. package/dist/lib/node-esm/{chunk-6GSTEN7N.mjs.map → chunk-7F3BRKP7.mjs.map} +4 -4
  19. package/dist/lib/node-esm/index.mjs +8 -13
  20. package/dist/lib/node-esm/index.mjs.map +3 -3
  21. package/dist/lib/node-esm/meta.json +1 -1
  22. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  23. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
  24. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts +1 -0
  25. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts +2 -0
  27. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/SheetObjectSettings.d.ts +7 -0
  29. package/dist/types/src/components/SheetObjectSettings.d.ts.map +1 -0
  30. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  31. package/dist/types/src/components/index.d.ts +1 -0
  32. package/dist/types/src/components/index.d.ts.map +1 -1
  33. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -1
  34. package/dist/types/src/testing/playwright/playwright.config.d.ts +3 -0
  35. package/dist/types/src/testing/playwright/playwright.config.d.ts.map +1 -0
  36. package/dist/types/src/testing/playwright/sheet-manager.d.ts +24 -0
  37. package/dist/types/src/testing/playwright/sheet-manager.d.ts.map +1 -0
  38. package/dist/types/src/testing/playwright/sheet.spec.d.ts +2 -0
  39. package/dist/types/src/testing/playwright/sheet.spec.d.ts.map +1 -0
  40. package/package.json +39 -37
  41. package/src/SheetPlugin.tsx +5 -13
  42. package/src/components/GridSheet/GridSheet.stories.tsx +2 -0
  43. package/src/components/GridSheet/GridSheet.tsx +12 -3
  44. package/src/components/GridSheet/SheetCellEditor.stories.tsx +2 -2
  45. package/src/components/SheetContainer/SheetContainer.stories.tsx +44 -2
  46. package/src/components/SheetObjectSettings.tsx +38 -0
  47. package/src/components/Toolbar/Toolbar.tsx +7 -1
  48. package/src/components/index.ts +1 -0
  49. package/src/compute-graph/compute-graph.stories.tsx +1 -1
  50. package/src/compute-graph/compute-graph.test.ts +1 -1
  51. package/src/integrations/thread-ranges.ts +2 -0
  52. package/src/sanity.test.ts +1 -1
  53. package/src/testing/playwright/playwright.config.ts +18 -0
  54. package/src/testing/playwright/sheet-manager.ts +91 -0
  55. package/src/testing/playwright/sheet.spec.ts +78 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-sheet",
3
- "version": "0.7.1",
3
+ "version": "0.7.2-main.f1adc9f",
4
4
  "description": "Braneframe sketch plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -69,33 +69,33 @@
69
69
  "re-resizable": "^6.9.17",
70
70
  "react-markdown": "^8.0.5",
71
71
  "react-resize-detector": "^11.0.1",
72
- "@dxos/client": "0.7.1",
73
- "@dxos/app-framework": "0.7.1",
74
- "@dxos/async": "0.7.1",
75
- "@dxos/crypto": "0.7.1",
76
- "@dxos/debug": "0.7.1",
77
- "@dxos/context": "0.7.1",
78
- "@dxos/echo-schema": "0.7.1",
79
- "@dxos/invariant": "0.7.1",
80
- "@dxos/log": "0.7.1",
81
- "@dxos/plugin-attention": "0.7.1",
82
- "@dxos/plugin-client": "0.7.1",
83
- "@dxos/keys": "0.7.1",
84
- "@dxos/plugin-markdown": "0.7.1",
85
- "@dxos/plugin-stack": "0.7.1",
86
- "@dxos/plugin-graph": "0.7.1",
87
- "@dxos/plugin-script": "0.7.1",
88
- "@dxos/plugin-space": "0.7.1",
89
- "@dxos/react-client": "0.7.1",
90
- "@dxos/react-ui-attention": "0.7.1",
91
- "@dxos/react-hooks": "0.7.1",
92
- "@dxos/react-ui-grid": "0.7.1",
93
- "@dxos/react-ui-form": "0.7.1",
94
- "@dxos/react-ui-editor": "0.7.1",
95
- "@dxos/react-ui-list": "0.7.1",
96
- "@dxos/schema": "0.7.1",
97
- "@dxos/react-ui-stack": "0.7.1",
98
- "@dxos/util": "0.7.1"
72
+ "@dxos/app-framework": "0.7.2-main.f1adc9f",
73
+ "@dxos/client": "0.7.2-main.f1adc9f",
74
+ "@dxos/context": "0.7.2-main.f1adc9f",
75
+ "@dxos/async": "0.7.2-main.f1adc9f",
76
+ "@dxos/crypto": "0.7.2-main.f1adc9f",
77
+ "@dxos/debug": "0.7.2-main.f1adc9f",
78
+ "@dxos/invariant": "0.7.2-main.f1adc9f",
79
+ "@dxos/echo-schema": "0.7.2-main.f1adc9f",
80
+ "@dxos/keys": "0.7.2-main.f1adc9f",
81
+ "@dxos/log": "0.7.2-main.f1adc9f",
82
+ "@dxos/plugin-client": "0.7.2-main.f1adc9f",
83
+ "@dxos/plugin-attention": "0.7.2-main.f1adc9f",
84
+ "@dxos/plugin-graph": "0.7.2-main.f1adc9f",
85
+ "@dxos/plugin-script": "0.7.2-main.f1adc9f",
86
+ "@dxos/plugin-markdown": "0.7.2-main.f1adc9f",
87
+ "@dxos/plugin-space": "0.7.2-main.f1adc9f",
88
+ "@dxos/plugin-stack": "0.7.2-main.f1adc9f",
89
+ "@dxos/react-client": "0.7.2-main.f1adc9f",
90
+ "@dxos/react-hooks": "0.7.2-main.f1adc9f",
91
+ "@dxos/react-ui-attention": "0.7.2-main.f1adc9f",
92
+ "@dxos/react-ui-editor": "0.7.2-main.f1adc9f",
93
+ "@dxos/react-ui-list": "0.7.2-main.f1adc9f",
94
+ "@dxos/react-ui-grid": "0.7.2-main.f1adc9f",
95
+ "@dxos/react-ui-stack": "0.7.2-main.f1adc9f",
96
+ "@dxos/util": "0.7.2-main.f1adc9f",
97
+ "@dxos/react-ui-form": "0.7.2-main.f1adc9f",
98
+ "@dxos/schema": "0.7.2-main.f1adc9f"
99
99
  },
100
100
  "devDependencies": {
101
101
  "@lezer/generator": "^1.7.1",
@@ -107,19 +107,21 @@
107
107
  "react": "~18.2.0",
108
108
  "react-dom": "~18.2.0",
109
109
  "vite": "5.4.7",
110
- "@dxos/echo-generator": "0.7.1",
111
- "@dxos/react-ui": "0.7.1",
112
- "@dxos/react-ui-syntax-highlighter": "0.7.1",
113
- "@dxos/random": "0.7.1",
114
- "@dxos/react-ui-types": "0.7.1",
115
- "@dxos/storybook-utils": "0.7.1",
116
- "@dxos/react-ui-theme": "0.7.1"
110
+ "@dxos/lit-grid": "0.7.2-main.f1adc9f",
111
+ "@dxos/random": "0.7.2-main.f1adc9f",
112
+ "@dxos/echo-generator": "0.7.2-main.f1adc9f",
113
+ "@dxos/react-ui-syntax-highlighter": "0.7.2-main.f1adc9f",
114
+ "@dxos/react-ui": "0.7.2-main.f1adc9f",
115
+ "@dxos/react-ui-theme": "0.7.2-main.f1adc9f",
116
+ "@dxos/react-ui-types": "0.7.2-main.f1adc9f",
117
+ "@dxos/storybook-utils": "0.7.2-main.f1adc9f",
118
+ "@dxos/test-utils": "0.7.2-main.f1adc9f"
117
119
  },
118
120
  "peerDependencies": {
119
121
  "react": "~18.2.0",
120
122
  "react-dom": "~18.2.0",
121
- "@dxos/react-ui": "0.7.1",
122
- "@dxos/react-ui-theme": "0.7.1"
123
+ "@dxos/react-ui-theme": "0.7.2-main.f1adc9f",
124
+ "@dxos/react-ui": "0.7.2-main.f1adc9f"
123
125
  },
124
126
  "publishConfig": {
125
127
  "access": "public"
@@ -13,7 +13,7 @@ import { SpaceAction } from '@dxos/plugin-space';
13
13
  import { getSpace, isEchoObject } from '@dxos/react-client/echo';
14
14
  import { Icon } from '@dxos/react-ui';
15
15
 
16
- import { ComputeGraphContextProvider, RangeList, SheetContainer } from './components';
16
+ import { ComputeGraphContextProvider, SheetContainer, SheetObjectSettings } from './components';
17
17
  import { type ComputeGraphRegistry } from './compute-graph';
18
18
  import { compareIndexPositions, createSheet } from './defs';
19
19
  import { computeGraphFacet } from './extensions';
@@ -144,26 +144,18 @@ export const SheetPlugin = (): PluginDefinition<SheetPluginProvides> => {
144
144
  surface: {
145
145
  component: ({ data, role }) => {
146
146
  const space = isEchoObject(data.object) ? getSpace(data.object) : undefined;
147
- if (!space) {
148
- return null;
149
- }
150
-
151
147
  switch (role) {
152
148
  case 'article':
153
149
  case 'section':
154
- if (data.object instanceof SheetType) {
150
+ if (space && data.object instanceof SheetType) {
155
151
  return <SheetContainer space={space} sheet={data.object} role={role} />;
156
152
  }
157
-
158
- return null;
153
+ break;
159
154
  case 'complementary--settings':
160
155
  if (data.subject instanceof SheetType) {
161
- return {
162
- node: <RangeList sheet={data.subject} />,
163
- };
156
+ return <SheetObjectSettings sheet={data.subject} />;
164
157
  }
165
-
166
- return null;
158
+ break;
167
159
  }
168
160
 
169
161
  return null;
@@ -2,6 +2,8 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import '@dxos-theme';
6
+
5
7
  import { type Meta } from '@storybook/react';
6
8
  import React from 'react';
7
9
 
@@ -335,21 +335,30 @@ export const GridSheet = () => {
335
335
  <DropdownMenu.VirtualTrigger virtualRef={contextMenuAnchorRef} />
336
336
  <DropdownMenu.Content side={contextMenuAxis === 'col' ? 'bottom' : 'right'} sideOffset={4} collisionPadding={8}>
337
337
  <DropdownMenu.Viewport>
338
- <DropdownMenu.Item onClick={() => handleAxisMenuAction('insert-before')}>
338
+ <DropdownMenu.Item
339
+ onClick={() => handleAxisMenuAction('insert-before')}
340
+ data-testid={`grid.${contextMenuAxis}.insert-before`}
341
+ >
339
342
  <Icon
340
343
  size={5}
341
344
  icon={contextMenuAxis === 'col' ? 'ph--columns-plus-left--regular' : 'ph--rows-plus-top--regular'}
342
345
  />
343
346
  <span>{t(`add ${contextMenuAxis} before label`)}</span>
344
347
  </DropdownMenu.Item>
345
- <DropdownMenu.Item onClick={() => handleAxisMenuAction('insert-after')}>
348
+ <DropdownMenu.Item
349
+ onClick={() => handleAxisMenuAction('insert-after')}
350
+ data-testid={`grid.${contextMenuAxis}.insert-after`}
351
+ >
346
352
  <Icon
347
353
  size={5}
348
354
  icon={contextMenuAxis === 'col' ? 'ph--columns-plus-right--regular' : 'ph--rows-plus-bottom--regular'}
349
355
  />
350
356
  <span>{t(`add ${contextMenuAxis} after label`)}</span>
351
357
  </DropdownMenu.Item>
352
- <DropdownMenu.Item onClick={() => handleAxisMenuAction('drop')}>
358
+ <DropdownMenu.Item
359
+ onClick={() => handleAxisMenuAction('drop')}
360
+ data-testid={`grid.${contextMenuAxis}.drop`}
361
+ >
353
362
  <Icon size={5} icon='ph--backspace--regular' />
354
363
  <span>{t(`delete ${contextMenuAxis} label`)}</span>
355
364
  </DropdownMenu.Item>
@@ -8,7 +8,7 @@ import { type Meta } from '@storybook/react';
8
8
  import React, { useEffect, useMemo, useState } from 'react';
9
9
 
10
10
  import { Client } from '@dxos/client';
11
- import { createDocAccessor, type EchoReactiveObject } from '@dxos/client/echo';
11
+ import { createDocAccessor, type ReactiveEchoObject } from '@dxos/client/echo';
12
12
  import { automerge } from '@dxos/react-ui-editor';
13
13
  import { CellEditor, type CellEditorProps } from '@dxos/react-ui-grid';
14
14
  import { withTheme } from '@dxos/storybook-utils';
@@ -33,7 +33,7 @@ const Story = ({ value, ...props }: StoryProps) => {
33
33
 
34
34
  const AutomergeStory = ({ value, ...props }: StoryProps) => {
35
35
  const cell = 'A1';
36
- const [object, setObject] = useState<EchoReactiveObject<SheetType>>();
36
+ const [object, setObject] = useState<ReactiveEchoObject<SheetType>>();
37
37
  useEffect(() => {
38
38
  setTimeout(async () => {
39
39
  const client = new Client({ types: [SheetType] });
@@ -2,18 +2,40 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import '@dxos-theme';
6
+
5
7
  import { type Meta } from '@storybook/react';
6
8
  import React from 'react';
7
9
 
8
- import { useSpace } from '@dxos/react-client/echo';
10
+ import { type Intent, IntentProvider } from '@dxos/app-framework';
11
+ import { useSpace, create } from '@dxos/react-client/echo';
9
12
  import { withClientProvider } from '@dxos/react-client/testing';
10
13
  import { withTheme, withLayout } from '@dxos/storybook-utils';
11
14
 
12
15
  import { SheetContainer } from './SheetContainer';
13
16
  import { createTestCells, useTestSheet, withComputeGraphDecorator } from '../../testing';
14
17
  import translations from '../../translations';
15
- import { SheetType } from '../../types';
18
+ import { SheetAction, SheetType } from '../../types';
16
19
  import { useComputeGraph } from '../ComputeGraph';
20
+ import { RangeList } from '../RangeList';
21
+
22
+ // TODO(thure via wittjosiah): stories/components should be written such that the dependency on intents is external and provided via callback and then the story can implement it differently.
23
+ const storybookIntentValue = create({
24
+ dispatch: async (intents: Intent | Intent[]) => {
25
+ const intent = Array.isArray(intents) ? intents[0] : intents;
26
+ switch (intent.action) {
27
+ case SheetAction.DROP_AXIS: {
28
+ if (!intent.undo) {
29
+ const { model, axis, axisIndex } = intent.data as SheetAction.DropAxis;
30
+ model[axis === 'col' ? 'dropColumn' : 'dropRow'](axisIndex);
31
+ }
32
+ }
33
+ }
34
+ },
35
+ undo: async () => ({}),
36
+ history: [],
37
+ registerResolver: () => () => {},
38
+ });
17
39
 
18
40
  export const Basic = () => {
19
41
  const space = useSpace();
@@ -26,6 +48,26 @@ export const Basic = () => {
26
48
  return <SheetContainer space={space} sheet={sheet} role='article' ignoreAttention />;
27
49
  };
28
50
 
51
+ export const Spec = () => {
52
+ const space = useSpace();
53
+ const graph = useComputeGraph(space);
54
+ const sheet = useTestSheet(space, graph, { cells: { A1: { value: 'Ready' } } });
55
+ if (!sheet || !space) {
56
+ return null;
57
+ }
58
+
59
+ return (
60
+ <IntentProvider value={storybookIntentValue}>
61
+ <div role='none' className='grid grid-rows-[66%_33%] grid-cols-1'>
62
+ <SheetContainer space={space} sheet={sheet} role='article' ignoreAttention />
63
+ <div role='none' data-testid='grid.range-list'>
64
+ <RangeList sheet={sheet} />
65
+ </div>
66
+ </div>
67
+ </IntentProvider>
68
+ );
69
+ };
70
+
29
71
  const meta: Meta = {
30
72
  title: 'plugins/plugin-sheet/SheetContainer',
31
73
  component: SheetContainer,
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { SPACE_PLUGIN } from '@dxos/plugin-space/meta';
8
+ import { Input, useTranslation } from '@dxos/react-ui';
9
+
10
+ import { RangeList } from './RangeList';
11
+ import { type SheetType } from '../types';
12
+
13
+ export type SheetObjectSettingsProps = {
14
+ sheet: SheetType;
15
+ };
16
+
17
+ export const SheetObjectSettings = ({ sheet }: SheetObjectSettingsProps) => {
18
+ const { t } = useTranslation(SPACE_PLUGIN);
19
+
20
+ // TODO(burdon): Standardize forms.
21
+ return (
22
+ <>
23
+ <div role='form' className='flex flex-col w-full p-2 gap-1'>
24
+ <Input.Root>
25
+ <Input.Label>{t('name label')}</Input.Label>
26
+ <Input.TextInput
27
+ placeholder={t('name placeholder')}
28
+ value={sheet.name ?? ''}
29
+ onChange={(event) => {
30
+ sheet.name = event.target.value;
31
+ }}
32
+ />
33
+ </Input.Root>
34
+ </div>
35
+ <RangeList sheet={sheet} />
36
+ </>
37
+ );
38
+ };
@@ -225,7 +225,13 @@ const Alignment = () => {
225
225
  onValueChange={(value: AlignValue) => onAction?.({ key: alignKey, value })}
226
226
  >
227
227
  {alignmentOptions.map(({ value, icon }) => (
228
- <ToolbarItem itemType='toggleGroupItem' key={value} value={value} icon={icon}>
228
+ <ToolbarItem
229
+ itemType='toggleGroupItem'
230
+ key={value}
231
+ value={value}
232
+ icon={icon}
233
+ data-testid={`grid.toolbar.${alignKey}.${value}`}
234
+ >
229
235
  {t('toolbar action label', {
230
236
  key: t(`range key ${alignKey} label`),
231
237
  value: t(`range value ${value} label`),
@@ -8,5 +8,6 @@ export * from './ComputeGraph';
8
8
  export * from './GridSheet';
9
9
  export * from './RangeList';
10
10
  export * from './SheetContext';
11
+ export * from './SheetObjectSettings';
11
12
 
12
13
  export const SheetContainer = lazy(() => import('./SheetContainer'));
@@ -45,7 +45,7 @@ const Story = () => {
45
45
  setResult({ functions: { standard: f1.length, echo: f2.length } });
46
46
  });
47
47
 
48
- space.db.add(create(FunctionType, { version: 1, binding: FUNCTION_NAME }));
48
+ space.db.add(create(FunctionType, { name: 'test', version: 1, binding: FUNCTION_NAME }));
49
49
  }
50
50
  }, [space, graph]);
51
51
 
@@ -30,7 +30,7 @@ describe('ComputeGraph', () => {
30
30
  // Create script.
31
31
  const trigger = new Trigger();
32
32
  graph.update.once(() => trigger.wake());
33
- const functionObject = space.db.add(create(FunctionType, { version: 1, binding: 'TEST' }));
33
+ const functionObject = space.db.add(create(FunctionType, { name: 'test', version: 1, binding: 'TEST' }));
34
34
  await trigger.wait();
35
35
  const functions = graph.getFunctions({ echo: true });
36
36
  expect(functions).to.toHaveLength(1);
@@ -43,6 +43,8 @@ export const useUpdateFocusedCellOnThreadSelection = (grid: DxGridElement | null
43
43
  // TODO(Zan): Everywhere we refer to the cursor in a thread context should change to `anchor`.
44
44
  const range = parseThreadAnchorAsCellRange(data.cursor);
45
45
  range && grid?.setFocus({ ...range.to, plane: 'grid' }, true);
46
+
47
+ return { data: true };
46
48
  }
47
49
  }
48
50
  },
@@ -34,7 +34,7 @@ describe('test', () => {
34
34
  // - ERROR "process.nextTick is not a function"
35
35
  // - ERROR "Identifier 'Buffer' has already been declared" if { nodeExternal: true }
36
36
  const space = await client.spaces.create();
37
- const fn = space.db.add(create(FunctionType, { version: 1, binding: 'HELLO' }));
37
+ const fn = space.db.add(create(FunctionType, { name: 'test', version: 1, binding: 'HELLO' }));
38
38
  expect(fn).to.exist;
39
39
  });
40
40
  });
@@ -0,0 +1,18 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { nxE2EPreset } from '@nx/playwright/preset';
6
+ import { defineConfig } from '@playwright/test';
7
+
8
+ import { e2ePreset } from '@dxos/test-utils/playwright';
9
+
10
+ export default defineConfig({
11
+ ...nxE2EPreset(__filename, { testDir: __dirname }),
12
+ ...e2ePreset(__dirname),
13
+ webServer: {
14
+ command: 'pnpm -w nx storybook stories',
15
+ port: 9009,
16
+ reuseExistingServer: !process.env.CI,
17
+ },
18
+ });
@@ -0,0 +1,91 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Locator, type Page } from '@playwright/test';
6
+
7
+ import { DxGridManager } from '@dxos/lit-grid/testing';
8
+ import { type DxGridPosition, type DxGridAxis } from '@dxos/react-ui-grid';
9
+
10
+ /**
11
+ * Test helper for managing dx-grid interactions and assertions in Playwright tests.
12
+ * Provides utilities for cell selection, grid navigation, virtualization checks and event handling.
13
+ */
14
+ export class SheetManager {
15
+ constructor(page: Page, grid?: Locator) {
16
+ this.grid = new DxGridManager(page, grid);
17
+ this.page = page;
18
+ }
19
+
20
+ grid: DxGridManager;
21
+ page: Page;
22
+
23
+ async ready() {
24
+ await this.grid.ready();
25
+ return this.cellByText('Ready').waitFor({ state: 'visible' });
26
+ }
27
+
28
+ async fill(text: string) {
29
+ // TODO(thure): Do these timeouts help with test flakiness?
30
+ await this.page.waitForTimeout(200);
31
+ await this.cellEditor().fill(text);
32
+ await this.page.waitForTimeout(200);
33
+ }
34
+
35
+ async press(key: string) {
36
+ // TODO(thure): Does these timeouts help with test flakiness?
37
+ await this.page.waitForTimeout(200);
38
+ await this.page.keyboard.press(key);
39
+ await this.page.waitForTimeout(200);
40
+ }
41
+
42
+ async commit(key: string) {
43
+ // TODO(thure): Why do we need to wait? Enter is ignored otherwise…
44
+ await this.page.waitForTimeout(500);
45
+ await this.press(key);
46
+ }
47
+
48
+ cellByText(text: string) {
49
+ return this.grid.grid.getByText(text);
50
+ }
51
+
52
+ async setFocusedCellValue(text: string, commitKey: string) {
53
+ const mode = await this.grid.mode();
54
+ if (mode === 'browse') {
55
+ await this.commit('Enter');
56
+ }
57
+ await this.fill(text);
58
+ await this.commit(commitKey);
59
+ }
60
+
61
+ async selectRange(start: DxGridPosition, end: DxGridPosition) {
62
+ const startCell = this.grid.cell(start.col, start.row, start.plane);
63
+ const endCell = this.grid.cell(end.col, end.row, end.plane);
64
+ const startBox = await startCell.boundingBox();
65
+ const endBox = await endCell.boundingBox();
66
+ await startCell.dragTo(endCell, {
67
+ sourcePosition: { x: startBox!.width / 2, y: startBox!.height / 2 },
68
+ targetPosition: { x: endBox!.width / 2, y: endBox!.height / 2 },
69
+ });
70
+ }
71
+
72
+ async deleteAxis(axis: DxGridAxis, position: number) {
73
+ const col = axis === 'row' ? 0 : position;
74
+ const row = axis === 'row' ? position : 0;
75
+ const plane = axis === 'row' ? 'frozenColsStart' : 'frozenRowsStart';
76
+ await this.grid.cell(col, row, plane).click({ button: 'right' });
77
+ await this.page.getByTestId(`grid.${axis}.drop`).click();
78
+ }
79
+
80
+ toolbarAction(key: string, value: string) {
81
+ return this.page.getByTestId(`grid.toolbar.${key}.${value}`);
82
+ }
83
+
84
+ cellEditor() {
85
+ return this.page.getByTestId('grid.cell-editor').getByRole('textbox');
86
+ }
87
+
88
+ rangeInList(a1Coords: string) {
89
+ return this.page.getByTestId('grid.range-list').getByText(a1Coords);
90
+ }
91
+ }
@@ -0,0 +1,78 @@
1
+ //
2
+ // Copyright 2021 DXOS.org
3
+ //
4
+
5
+ import { expect, test, type Page } from '@playwright/test';
6
+
7
+ import { faker } from '@dxos/random';
8
+ import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
9
+
10
+ import { SheetManager } from './sheet-manager';
11
+
12
+ test.describe('plugin-sheet', () => {
13
+ let page: Page;
14
+ let sheet: SheetManager;
15
+
16
+ test.beforeEach(async ({ browser }) => {
17
+ const setup = await setupPage(browser, {
18
+ url: storybookUrl('plugins-plugin-sheet-sheetcontainer--spec'),
19
+ });
20
+ page = setup.page;
21
+ sheet = new SheetManager(page);
22
+ await sheet.ready();
23
+ });
24
+
25
+ test.afterEach(async () => {
26
+ await page.close();
27
+ });
28
+
29
+ test('basic interactions', async () => {
30
+ // Cell editor should initially be hidden
31
+ await expect(sheet.cellEditor()).not.toBeVisible();
32
+ // Click on cell to focus it
33
+ await sheet.grid.cell(0, 0, 'grid').click();
34
+ // Cell editor should still be hidden
35
+ await expect(sheet.cellEditor()).not.toBeVisible();
36
+ // Click again to edit it
37
+ await sheet.grid.cell(0, 0, 'grid').click();
38
+ // Confirm editor displays
39
+ await expect(sheet.cellEditor()).toBeVisible();
40
+ // Type in a value and press enter
41
+ const testString = faker.string.uuid();
42
+ await sheet.setFocusedCellValue(testString, 'Enter');
43
+ // Expect that value to now show in the grid
44
+ await expect(sheet.cellByText(testString)).toBeVisible();
45
+ });
46
+
47
+ test('functions, row deletion, indexed positions', async () => {
48
+ const firstNumber = 123;
49
+ const secondNumber = 789;
50
+ const thirdNumber = 567;
51
+ // Input numbers
52
+ await sheet.grid.cell(0, 2, 'grid').click();
53
+ await sheet.setFocusedCellValue(`${firstNumber}`, 'Enter');
54
+ await sheet.setFocusedCellValue(`${secondNumber}`, 'Enter');
55
+ await sheet.setFocusedCellValue(`${thirdNumber}`, 'Enter');
56
+
57
+ // Test range input
58
+ await sheet.press('Enter');
59
+ await sheet.fill('=SUM(');
60
+ await sheet.selectRange({ col: 0, row: 2, plane: 'grid' }, { col: 0, row: 4, plane: 'grid' });
61
+ await sheet.press(')');
62
+ await sheet.commit('Enter');
63
+ // Check sum
64
+ await expect(sheet.grid.cell(0, 5, 'grid')).toHaveText(`${firstNumber + secondNumber + thirdNumber}`);
65
+ // Delete row of second number
66
+ await sheet.deleteAxis('row', 3);
67
+ // Check sum again, it should be one cell up and reflect the updated range.
68
+ await expect(sheet.grid.cell(0, 4, 'grid')).toHaveText(`${firstNumber + thirdNumber}`);
69
+ });
70
+
71
+ test('ranges', async () => {
72
+ await sheet.selectRange({ col: 1, row: 0, plane: 'grid' }, { col: 1, row: 1, plane: 'grid' });
73
+ await sheet.toolbarAction('alignment', 'center').click();
74
+ expect(await sheet.grid.cell(1, 0, 'grid')).toHaveAttribute('class', /text-center/);
75
+ expect(await sheet.grid.cell(1, 1, 'grid')).toHaveAttribute('class', /text-center/);
76
+ await expect(sheet.rangeInList('B1:B2')).toBeVisible();
77
+ });
78
+ });