@dxos/plugin-navtree 0.8.3-staging.0fa589b → 0.8.4-main.1068cf700f

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 (185) hide show
  1. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs +44 -0
  2. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-63YLDLLE.mjs +1373 -0
  4. package/dist/lib/browser/chunk-63YLDLLE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-X2HDG62A.mjs +23 -0
  6. package/dist/lib/browser/chunk-X2HDG62A.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-YKTYPINZ.mjs +19 -0
  8. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +89 -100
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/{keyboard-DDFATRUG.mjs → keyboard-46XQHY2O.mjs} +21 -19
  12. package/dist/lib/browser/keyboard-46XQHY2O.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/operation-resolver-NNNDII45.mjs +53 -0
  15. package/dist/lib/browser/operation-resolver-NNNDII45.mjs.map +7 -0
  16. package/dist/lib/browser/react-surface-APSEG23H.mjs +59 -0
  17. package/dist/lib/browser/react-surface-APSEG23H.mjs.map +7 -0
  18. package/dist/lib/browser/state-M5YUCIQU.mjs +121 -0
  19. package/dist/lib/browser/state-M5YUCIQU.mjs.map +7 -0
  20. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  21. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
  23. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
  24. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
  25. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
  26. package/dist/types/src/capabilities/index.d.ts +5 -105
  27. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  28. package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
  29. package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
  30. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
  31. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
  32. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  33. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  35. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
  37. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
  38. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
  39. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/state/index.d.ts +3 -0
  41. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/state/state.d.ts +6 -0
  43. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  44. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +3 -2
  45. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
  46. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  47. package/dist/types/src/components/NavTree/NavTree.d.ts +32 -3
  48. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  49. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +29 -4
  50. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/NavTreeContainer.d.ts +8 -1
  52. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  53. package/dist/types/src/components/NavTreeDocumentTitle.d.ts +1 -1
  54. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
  55. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
  56. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  57. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +33 -4
  58. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  59. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +2 -2
  60. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  61. package/dist/types/src/components/Sidebar/L0Menu.d.ts +6 -4
  62. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  63. package/dist/types/src/components/Sidebar/L1Panel.d.ts +6 -7
  64. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  65. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +12 -0
  66. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -0
  67. package/dist/types/src/components/Sidebar/index.d.ts +1 -0
  68. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -1
  69. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  70. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
  71. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  72. package/dist/types/src/components/index.d.ts +1 -1
  73. package/dist/types/src/components/index.d.ts.map +1 -1
  74. package/dist/types/src/components/types.d.ts +8 -8
  75. package/dist/types/src/components/types.d.ts.map +1 -1
  76. package/dist/types/src/experimental/Tree.d.ts +4 -5
  77. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  78. package/dist/types/src/experimental/Tree.stories.d.ts +23 -6
  79. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  80. package/dist/types/src/hooks.d.ts +32 -1
  81. package/dist/types/src/hooks.d.ts.map +1 -1
  82. package/dist/types/src/index.d.ts +1 -2
  83. package/dist/types/src/index.d.ts.map +1 -1
  84. package/dist/types/src/meta.d.ts +3 -4
  85. package/dist/types/src/meta.d.ts.map +1 -1
  86. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  87. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  88. package/dist/types/src/testing/generator.d.ts +5 -6
  89. package/dist/types/src/testing/generator.d.ts.map +1 -1
  90. package/dist/types/src/testing/update-state.d.ts +2 -2
  91. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  92. package/dist/types/src/translations.d.ts +14 -13
  93. package/dist/types/src/translations.d.ts.map +1 -1
  94. package/dist/types/src/types/capabilities.d.ts +16 -0
  95. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  96. package/dist/types/src/types/events.d.ts +5 -0
  97. package/dist/types/src/types/events.d.ts.map +1 -0
  98. package/dist/types/src/types/index.d.ts +4 -0
  99. package/dist/types/src/types/index.d.ts.map +1 -0
  100. package/dist/types/src/{types.d.ts → types/schema.d.ts} +10 -9
  101. package/dist/types/src/types/schema.d.ts.map +1 -0
  102. package/dist/types/src/util.d.ts +8 -9
  103. package/dist/types/src/util.d.ts.map +1 -1
  104. package/dist/types/tsconfig.tsbuildinfo +1 -1
  105. package/package.json +56 -55
  106. package/src/NavTreePlugin.tsx +64 -84
  107. package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
  108. package/src/capabilities/app-graph-builder/index.ts +7 -0
  109. package/src/capabilities/index.ts +5 -9
  110. package/src/capabilities/keyboard/index.ts +7 -0
  111. package/src/capabilities/keyboard/keyboard.ts +66 -0
  112. package/src/capabilities/operation-resolver/index.ts +7 -0
  113. package/src/capabilities/operation-resolver/operation-resolver.ts +42 -0
  114. package/src/capabilities/react-surface/index.ts +7 -0
  115. package/src/capabilities/react-surface/react-surface.tsx +55 -0
  116. package/src/capabilities/state/index.ts +7 -0
  117. package/src/capabilities/state/state.ts +122 -0
  118. package/src/components/CommandsDialog/CommandsDialog.tsx +108 -98
  119. package/src/components/CommandsDialog/CommandsTrigger.tsx +8 -19
  120. package/src/components/NavTree/NavTree.stories.tsx +100 -94
  121. package/src/components/NavTree/NavTree.tsx +12 -18
  122. package/src/components/NavTreeContainer.tsx +283 -259
  123. package/src/components/NavTreeDocumentTitle.tsx +5 -4
  124. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +21 -13
  125. package/src/components/NavTreeItem/NavTreeItemAction.tsx +28 -21
  126. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +38 -32
  127. package/src/components/Sidebar/L0Menu.tsx +115 -143
  128. package/src/components/Sidebar/L1Panel.tsx +177 -122
  129. package/src/components/Sidebar/L1Tabs.tsx +33 -0
  130. package/src/components/Sidebar/index.ts +1 -0
  131. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -8
  132. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
  133. package/src/components/index.ts +2 -1
  134. package/src/components/types.ts +9 -8
  135. package/src/experimental/Tree.stories.tsx +77 -85
  136. package/src/experimental/Tree.tsx +17 -35
  137. package/src/hooks.ts +54 -2
  138. package/src/index.ts +1 -2
  139. package/src/meta.ts +11 -8
  140. package/src/testing/app-graph-builder.ts +159 -234
  141. package/src/testing/generator.ts +20 -18
  142. package/src/testing/update-state.ts +6 -6
  143. package/src/translations.ts +7 -4
  144. package/src/types/capabilities.ts +21 -0
  145. package/src/types/events.ts +12 -0
  146. package/src/types/index.ts +7 -0
  147. package/src/{types.ts → types/schema.ts} +9 -8
  148. package/src/util.ts +14 -35
  149. package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs +0 -45
  150. package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs.map +0 -7
  151. package/dist/lib/browser/chunk-33VLL4IJ.mjs +0 -15
  152. package/dist/lib/browser/chunk-33VLL4IJ.mjs.map +0 -7
  153. package/dist/lib/browser/chunk-IEW3JD5C.mjs +0 -16
  154. package/dist/lib/browser/chunk-IEW3JD5C.mjs.map +0 -7
  155. package/dist/lib/browser/chunk-ZJPL4XJD.mjs +0 -1429
  156. package/dist/lib/browser/chunk-ZJPL4XJD.mjs.map +0 -7
  157. package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs +0 -47
  158. package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs.map +0 -7
  159. package/dist/lib/browser/keyboard-DDFATRUG.mjs.map +0 -7
  160. package/dist/lib/browser/react-surface-H3JRTVNU.mjs +0 -51
  161. package/dist/lib/browser/react-surface-H3JRTVNU.mjs.map +0 -7
  162. package/dist/lib/browser/state-J6AKZRB4.mjs +0 -117
  163. package/dist/lib/browser/state-J6AKZRB4.mjs.map +0 -7
  164. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  165. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  166. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  167. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  168. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  169. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  170. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  171. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  172. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  173. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  174. package/dist/types/src/capabilities/state.d.ts +0 -20
  175. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  176. package/dist/types/src/events.d.ts +0 -4
  177. package/dist/types/src/events.d.ts.map +0 -1
  178. package/dist/types/src/types.d.ts.map +0 -1
  179. package/src/capabilities/app-graph-builder.ts +0 -52
  180. package/src/capabilities/capabilities.ts +0 -20
  181. package/src/capabilities/intent-resolver.ts +0 -37
  182. package/src/capabilities/keyboard.ts +0 -59
  183. package/src/capabilities/react-surface.tsx +0 -45
  184. package/src/capabilities/state.ts +0 -104
  185. package/src/events.ts +0 -11
@@ -2,45 +2,33 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type StoryObj, type Meta } from '@storybook/react';
8
- import { Schema } from 'effect';
9
- import React from 'react';
10
-
11
- import {
12
- Capabilities,
13
- IntentPlugin,
14
- LayoutAction,
15
- SettingsPlugin,
16
- contributes,
17
- createResolver,
18
- defineCapability,
19
- useCapability,
20
- } from '@dxos/app-framework';
5
+ import { Atom, type Registry } from '@effect-atom/atom-react';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import * as Effect from 'effect/Effect';
8
+ import React, { type KeyboardEvent, useCallback, useRef } from 'react';
9
+ import { expect, userEvent, within } from 'storybook/test';
10
+
11
+ import { Capabilities, Capability } from '@dxos/app-framework';
21
12
  import { withPluginManager } from '@dxos/app-framework/testing';
22
- import { live } from '@dxos/live-object';
23
- import { AttentionPlugin } from '@dxos/plugin-attention';
24
- import { ClientPlugin } from '@dxos/plugin-client';
25
- import { GraphPlugin } from '@dxos/plugin-graph';
26
- import { SpacePlugin } from '@dxos/plugin-space';
27
- import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
28
- import { ThemePlugin } from '@dxos/plugin-theme';
13
+ import { useAtomCapability } from '@dxos/app-framework/ui';
14
+ import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
15
+ import { OperationResolver } from '@dxos/operation';
16
+ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
29
17
  import { faker } from '@dxos/random';
30
18
  import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
31
- import { useAttentionAttributes, useAttention } from '@dxos/react-ui-attention';
19
+ import { withTheme } from '@dxos/react-ui/testing';
20
+ import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
32
21
  import { Stack, StackItem } from '@dxos/react-ui-stack';
33
- import { defaultTx, mx } from '@dxos/react-ui-theme';
34
- import { withLayout } from '@dxos/storybook-utils';
22
+ import { mx } from '@dxos/ui-theme';
35
23
 
36
24
  import { NavTreePlugin } from '../../NavTreePlugin';
37
25
  import { storybookGraphBuilders } from '../../testing';
38
- import translations from '../../translations';
26
+ import { translations } from '../../translations';
39
27
  import { NavTreeContainer } from '../NavTreeContainer';
40
28
 
41
29
  faker.seed(1234);
42
30
 
43
- const StoryState = defineCapability<{ tab: string }>('story-state');
31
+ const StoryState = Capability.make<Atom.Atom<{ tab: string }>>('story-state');
44
32
 
45
33
  // TODO(burdon): Fix outline (e.g., button in sidebar nav is clipped when focused).
46
34
  // TODO(burdon): Consider similar containment of: Table, Sheet, Kanban Column, Form, etc.
@@ -52,7 +40,7 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
52
40
  const { hasAttention } = useAttention(attendableId);
53
41
  console.log('hasAttention', hasAttention);
54
42
  return (
55
- <div className='flex p-1 items-center border-b border-separator'>
43
+ <div className='flex p-1 items-center border-be border-separator'>
56
44
  <IconButton
57
45
  density='coarse'
58
46
  icon='ph--atom--regular'
@@ -68,6 +56,15 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
68
56
 
69
57
  const StoryPlank = ({ attendableId }: { attendableId: string }) => {
70
58
  const attentionAttrs = useAttentionAttributes(attendableId);
59
+ const rootElement = useRef<HTMLDivElement | null>(null);
60
+
61
+ // NOTE(thure): This is the same workaround as in Plank, but that component is out of scope for this story.
62
+ // TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
63
+ const handleKeyDown = useCallback((event: KeyboardEvent) => {
64
+ if (event.target === event.currentTarget && event.key === 'Escape') {
65
+ rootElement.current?.closest('main')?.focus();
66
+ }
67
+ }, []);
71
68
 
72
69
  return (
73
70
  <StackItem.Root
@@ -75,10 +72,12 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
75
72
  {...attentionAttrs}
76
73
  classNames='bg-baseSurface border-ie border-separator'
77
74
  size={30}
75
+ onKeyDown={handleKeyDown}
76
+ ref={rootElement}
78
77
  >
79
78
  <StoryPlankHeading attendableId={attendableId} />
80
79
  <StackItem.Content toolbar>
81
- <Toolbar.Root classNames='border-b border-subduedSeparator'>
80
+ <Toolbar.Root classNames='border-be border-subduedSeparator'>
82
81
  <Toolbar.Button>Test</Toolbar.Button>
83
82
  </Toolbar.Root>
84
83
 
@@ -99,10 +98,10 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
99
98
  };
100
99
 
101
100
  const DefaultStory = () => {
102
- const state = useCapability(StoryState);
101
+ const state = useAtomCapability(StoryState);
103
102
 
104
103
  return (
105
- <Main.Root complementarySidebarState='closed'>
104
+ <Main.Root navigationSidebarState='expanded'>
106
105
  <Main.NavigationSidebar label='Navigation' classNames='grid'>
107
106
  <NavTreeContainer tab={state.tab} />
108
107
  </Main.NavigationSidebar>
@@ -116,85 +115,92 @@ const DefaultStory = () => {
116
115
  );
117
116
  };
118
117
 
119
- const meta: Meta<typeof NavTreeContainer> = {
118
+ const meta = {
120
119
  title: 'plugins/plugin-navtree/NavTree',
121
120
  component: NavTreeContainer,
122
121
  render: DefaultStory,
123
122
  decorators: [
123
+ withTheme(),
124
124
  withPluginManager({
125
125
  plugins: [
126
- ThemePlugin({ tx: defaultTx }),
127
- StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
128
- GraphPlugin(),
129
- IntentPlugin(),
130
- SettingsPlugin(),
131
- AttentionPlugin(),
126
+ ...corePlugins(),
127
+ StorybookPlugin({
128
+ initialState: { sidebarState: 'expanded' },
129
+ }),
130
+
132
131
  NavTreePlugin(),
133
132
  ],
134
- capabilities: (context) => [
135
- contributes(StoryState, live({ tab: 'space-0' })),
136
- contributes(Capabilities.AppGraphBuilder, storybookGraphBuilders(context)),
137
- contributes(Capabilities.IntentResolver, [
138
- createResolver({
139
- intent: LayoutAction.UpdateLayout,
140
- filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
141
- Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
142
- resolve: ({ subject }) => {
143
- const state = context.getCapability(StoryState);
144
- state.tab = subject;
145
- },
146
- }),
147
- ]),
148
- ],
133
+ capabilities: () => {
134
+ const storyStateAtom = Atom.make({ tab: 'space-0' }).pipe(Atom.keepAlive);
135
+ return [
136
+ Capability.contributes(StoryState, storyStateAtom),
137
+ Capability.contributes(AppCapabilities.AppGraphBuilder, storybookGraphBuilders()),
138
+ Capability.contributes(Capabilities.OperationResolver, [
139
+ OperationResolver.make({
140
+ operation: LayoutOperation.SwitchWorkspace,
141
+ handler: ({ subject }) =>
142
+ Effect.gen(function* () {
143
+ const registry: Registry.Registry = yield* Capability.get(Capabilities.AtomRegistry);
144
+ registry.set(storyStateAtom, { tab: subject });
145
+ }),
146
+ }),
147
+ ]),
148
+ ];
149
+ },
149
150
  }),
150
- withLayout({ fullscreen: true }),
151
151
  ],
152
152
  parameters: {
153
+ layout: 'fullscreen',
153
154
  translations,
154
155
  },
155
- };
156
+ } satisfies Meta<typeof NavTreeContainer>;
156
157
 
157
158
  export default meta;
158
159
 
159
160
  type Story = StoryObj<typeof NavTreeContainer>;
160
161
 
161
- export const Default: Story = {};
162
+ export const Default: Story = {
163
+ play: async ({ canvasElement }) => {
164
+ const canvas = within(canvasElement);
162
165
 
163
- // TODO(wittjosiah): Deduplicate plugins/capabilities with default story.
164
- export const WithClient: Story = {
165
- decorators: [
166
- withPluginManager({
167
- plugins: [
168
- ThemePlugin({ tx: defaultTx }),
169
- GraphPlugin(),
170
- IntentPlugin(),
171
- SettingsPlugin(),
172
- AttentionPlugin(),
173
- NavTreePlugin(),
174
- ClientPlugin({
175
- onClientInitialized: async (_, client) => {
176
- await client.halo.createIdentity();
177
- },
178
- }),
179
- SpacePlugin(),
180
- // Needs to be last so that the dialog and popovers have access to all contexts.
181
- StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
182
- ],
183
- capabilities: (context) => [
184
- contributes(StoryState, live({ tab: 'space-0' })),
185
- contributes(Capabilities.IntentResolver, [
186
- createResolver({
187
- intent: LayoutAction.UpdateLayout,
188
- filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
189
- Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
190
- resolve: ({ subject }) => {
191
- const state = context.getCapability(StoryState);
192
- state.tab = subject;
193
- },
194
- }),
195
- ]),
196
- ],
197
- }),
198
- withLayout({ fullscreen: true }),
199
- ],
166
+ // Find the element with treegrid role and click on its parent
167
+ const treegridElement = await canvas.findByRole('treegrid');
168
+ const treegridParent = treegridElement.parentElement;
169
+ if (treegridParent) {
170
+ await userEvent.click(treegridParent);
171
+ }
172
+
173
+ // Press Escape
174
+ await userEvent.keyboard('{Escape}');
175
+
176
+ // Confirm that focus is on an element with attribute data-main-landmark="0"
177
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
178
+
179
+ // Press Tab
180
+ await userEvent.keyboard('{Tab}');
181
+
182
+ // Confirm that focus is now on an element with data-main-landmark="1"
183
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
184
+
185
+ // Press Tab
186
+ await userEvent.keyboard('{Tab}');
187
+
188
+ // Confirm that focus is now on an element with data-main-landmark="0"
189
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
190
+
191
+ // Press Shift-Tab
192
+ await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
193
+
194
+ // Press Enter
195
+ await userEvent.keyboard('{Enter}');
196
+
197
+ // Confirm that focus is now on an element with data-attendable-id="space-0:object-0"
198
+ await expect(document.activeElement).toHaveAttribute('data-attendable-id', 'space-0:object-0');
199
+
200
+ // Press Escape
201
+ await userEvent.keyboard('{Escape}');
202
+
203
+ // Confirm that focus is now on an element with data-main-landmark="1"
204
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
205
+ },
200
206
  };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useMemo } from 'react';
5
+ import React, { forwardRef, useMemo } from 'react';
6
6
 
7
7
  import { type TreeProps } from '@dxos/react-ui-list';
8
8
  import { Tabs } from '@dxos/react-ui-tabs';
@@ -10,27 +10,17 @@ import { Tabs } from '@dxos/react-ui-tabs';
10
10
  import { useLoadDescendents } from '../../hooks';
11
11
  import { type NavTreeItemGraphNode } from '../../types';
12
12
  import { useNavTreeContext } from '../NavTreeContext';
13
- import { L0Menu, L1Panels, type L1PanelsProps } from '../Sidebar';
13
+ import { L0Menu, L1Tabs, type L1TabsProps } from '../Sidebar';
14
14
 
15
15
  export const NAV_TREE_ITEM = 'NavTreeItem';
16
16
 
17
- export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'> & Pick<L1PanelsProps, 'open'>;
17
+ export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'> & Pick<L1TabsProps, 'open'>;
18
18
 
19
- export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
19
+ export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, ...props }, forwardedRef) => {
20
20
  const { tab, useItems, onBack } = useNavTreeContext();
21
21
  const topLevelActions = useItems(root, { disposition: 'menu', sort: true });
22
- const topLevelCollections = useItems(root, { disposition: 'collection' });
23
22
  const topLevelWorkspaces = useItems(root, { disposition: 'workspace' });
24
- const topLevelNavigation = useItems(root, { disposition: 'navigation' });
25
- const l0Items = useMemo(
26
- () => [
27
- // prettier-ignore
28
- ...topLevelCollections,
29
- ...topLevelWorkspaces,
30
- ...topLevelNavigation,
31
- ],
32
- [topLevelCollections, topLevelWorkspaces, topLevelNavigation],
33
- );
23
+ const l0Items = topLevelWorkspaces;
34
24
  const pinnedItems = useItems(root, { disposition: 'pin-end', sort: true });
35
25
  const userAccountItem = useItems(root, { disposition: 'user-account' })[0];
36
26
  const topLevelItems = useMemo(
@@ -47,7 +37,11 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
47
37
  const path = useMemo(() => [id], [id]);
48
38
 
49
39
  return (
50
- <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
40
+ // TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
41
+ // it uses RovingFocus and doesn’t support moving focus to an item that is not a tab. Assess whether this situation
42
+ // should change including whether it should motivate a change in the design/taxonomy, or if this means this should
43
+ // not use `react-ui-tabs` at all.
44
+ <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative' ref={forwardedRef}>
51
45
  <L0Menu
52
46
  menuActions={topLevelActions}
53
47
  topLevelItems={l0Items}
@@ -56,7 +50,7 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
56
50
  path={path}
57
51
  parent={root}
58
52
  />
59
- <L1Panels topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
53
+ <L1Tabs topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
60
54
  </Tabs.Root>
61
55
  );
62
- };
56
+ });