@dxos/plugin-navtree 0.8.4-main.2e9d522 → 0.8.4-main.3c1ae3b
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.
- package/dist/lib/browser/{app-graph-builder-5J4LRWDX.mjs → app-graph-builder-OHSCN3GX.mjs} +13 -12
- package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-WEMCSQI7.mjs → chunk-5DWDYPGQ.mjs} +212 -189
- package/dist/lib/browser/chunk-5DWDYPGQ.mjs.map +7 -0
- package/dist/lib/browser/{chunk-WCARAZ7J.mjs → chunk-5VLWX4PK.mjs} +4 -4
- package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +7 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs +19 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +6 -10
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-3XYN2CBU.mjs → intent-resolver-YUVXVPHG.mjs} +7 -7
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
- package/dist/lib/browser/{keyboard-QRS6WOQH.mjs → keyboard-YJEK2HTH.mjs} +8 -10
- package/dist/lib/browser/{keyboard-QRS6WOQH.mjs.map → keyboard-YJEK2HTH.mjs.map} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-JSJ5SQX5.mjs → react-surface-RPXGIV45.mjs} +22 -16
- package/dist/lib/browser/react-surface-RPXGIV45.mjs.map +7 -0
- package/dist/lib/browser/{state-54DY3DOY.mjs → state-BFD5KJCV.mjs} +7 -7
- package/dist/lib/browser/state-BFD5KJCV.mjs.map +7 -0
- package/dist/types/src/NavTreePlugin.d.ts +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +18 -15
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +3 -3
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +3 -2
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts +30 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +29 -4
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeContainer.d.ts +8 -1
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +32 -4
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +2 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +3 -3
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts +1 -2
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +7 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +2 -2
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -2
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +49 -48
- package/src/NavTreePlugin.tsx +75 -76
- package/src/capabilities/app-graph-builder.ts +10 -9
- package/src/capabilities/capabilities.ts +2 -2
- package/src/capabilities/intent-resolver.ts +2 -2
- package/src/capabilities/keyboard.ts +3 -5
- package/src/capabilities/react-surface.tsx +16 -13
- package/src/capabilities/state.ts +5 -5
- package/src/components/CommandsDialog/CommandsDialog.tsx +102 -90
- package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
- package/src/components/NavTree/NavTree.stories.tsx +67 -53
- package/src/components/NavTree/NavTree.tsx +8 -4
- package/src/components/NavTreeContainer.tsx +313 -244
- package/src/components/NavTreeDocumentTitle.tsx +2 -2
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +4 -6
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +7 -6
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +31 -24
- package/src/components/Sidebar/L0Menu.tsx +56 -55
- package/src/components/Sidebar/L1Panel.tsx +23 -28
- package/src/components/Sidebar/L1Tabs.tsx +1 -0
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -8
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
- package/src/components/index.ts +1 -0
- package/src/components/types.ts +4 -3
- package/src/events.ts +2 -2
- package/src/experimental/Tree.stories.tsx +28 -30
- package/src/experimental/Tree.tsx +6 -23
- package/src/meta.ts +10 -7
- package/src/testing/app-graph-builder.ts +20 -19
- package/src/testing/generator.ts +10 -8
- package/src/types.ts +4 -3
- package/src/util.ts +2 -20
- package/dist/lib/browser/app-graph-builder-5J4LRWDX.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
- package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
- package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
- package/dist/lib/browser/chunk-WEMCSQI7.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-3XYN2CBU.mjs.map +0 -7
- package/dist/lib/browser/react-surface-JSJ5SQX5.mjs.map +0 -7
- package/dist/lib/browser/state-54DY3DOY.mjs.map +0 -7
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { useMemo, useState } from 'react';
|
|
5
|
+
import React, { forwardRef, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
8
|
+
import { useAppGraph, useIntentDispatcher } from '@dxos/app-framework/react';
|
|
8
9
|
import { type ActionLike, isAction, isActionGroup } from '@dxos/app-graph';
|
|
9
10
|
import { Keyboard, keySymbols } from '@dxos/keyboard';
|
|
10
11
|
import { useActions } from '@dxos/plugin-graph';
|
|
11
|
-
import { Button, Dialog, Icon,
|
|
12
|
+
import { Button, Dialog, Icon, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
12
13
|
import { SearchList } from '@dxos/react-ui-searchlist';
|
|
13
14
|
import {
|
|
14
15
|
cardDialogContent,
|
|
@@ -19,100 +20,111 @@ import {
|
|
|
19
20
|
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
20
21
|
import { getHostPlatform } from '@dxos/util';
|
|
21
22
|
|
|
22
|
-
import { KEY_BINDING,
|
|
23
|
+
import { KEY_BINDING, meta } from '../../meta';
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const [selected, setSelected] = useState<string | undefined>(initial);
|
|
28
|
-
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
29
|
-
const { graph } = useAppGraph();
|
|
25
|
+
export type CommandsDialogContentProps = {
|
|
26
|
+
selected?: string;
|
|
27
|
+
};
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
graph.traverse({
|
|
39
|
-
visitor: (node, path) => {
|
|
40
|
-
if (
|
|
41
|
-
(isAction(node) || isActionGroup(node)) &&
|
|
42
|
-
!actionMap.has(node.id) &&
|
|
43
|
-
current.startsWith(path.slice(0, -1).join('/'))
|
|
44
|
-
) {
|
|
45
|
-
actionMap.add(node.id);
|
|
46
|
-
actions.push(node);
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
});
|
|
29
|
+
// TODO(wittjosiah): This probably deserves its own plugin but for now it lives here w/ other navigation UI.
|
|
30
|
+
export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
|
|
31
|
+
({ selected: initial }, forwardedRef) => {
|
|
32
|
+
const { t } = useTranslation(meta.id);
|
|
33
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
34
|
+
const { graph } = useAppGraph();
|
|
35
|
+
const [selected, setSelected] = useState<string | undefined>(initial);
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
// Traverse graph.
|
|
38
|
+
// TODO(burdon): Factor out commonality with shortcut dialog.
|
|
39
|
+
const allActions = useMemo(() => {
|
|
40
|
+
// TODO(burdon): Get from navtree (not keyboard).
|
|
41
|
+
const current = Keyboard.singleton.getCurrentContext();
|
|
42
|
+
const actionMap = new Set<string>();
|
|
43
|
+
const actions: ActionLike[] = [];
|
|
44
|
+
graph.traverse({
|
|
45
|
+
visitor: (node, path) => {
|
|
46
|
+
if (
|
|
47
|
+
(isAction(node) || isActionGroup(node)) &&
|
|
48
|
+
!actionMap.has(node.id) &&
|
|
49
|
+
current.startsWith(path.slice(0, -1).join('/'))
|
|
50
|
+
) {
|
|
51
|
+
actionMap.add(node.id);
|
|
52
|
+
actions.push(node);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
actions.sort((a, b) => {
|
|
58
|
+
return toLocalizedString(a.properties.label, t)
|
|
59
|
+
?.toLowerCase()
|
|
60
|
+
.localeCompare(toLocalizedString(b.properties.label, t)?.toLowerCase());
|
|
61
|
+
});
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const actions = isActionGroup(group) ? groupActions : allActions;
|
|
63
|
+
return actions;
|
|
64
|
+
}, [graph]);
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
const group = allActions.find(({ id }) => id === selected);
|
|
67
|
+
const groupActions = useActions(graph, group?.id);
|
|
68
|
+
const actions = isActionGroup(group) ? groupActions : allActions;
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
<SearchList.Content classNames={cardDialogPaddedOverflow}>
|
|
72
|
-
{actions?.map((action) => {
|
|
73
|
-
const label = toLocalizedString(action.properties.label, t);
|
|
74
|
-
const shortcut =
|
|
75
|
-
typeof action.properties.keyBinding === 'string'
|
|
76
|
-
? action.properties.keyBinding
|
|
77
|
-
: action.properties.keyBinding?.[getHostPlatform()];
|
|
78
|
-
return (
|
|
79
|
-
<SearchList.Item
|
|
80
|
-
value={label}
|
|
81
|
-
key={action.id}
|
|
82
|
-
onSelect={() => {
|
|
83
|
-
if (action.properties.disabled) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
70
|
+
return (
|
|
71
|
+
<Dialog.Content classNames={cardDialogContent} ref={forwardedRef}>
|
|
72
|
+
<Dialog.Title classNames={cardDialogHeader}>{t('commands dialog title', { ns: meta.id })}</Dialog.Title>
|
|
86
73
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
<SearchList.Root label={t('command list input placeholder')} classNames={cardDialogSearchListRoot}>
|
|
75
|
+
<SearchList.Input placeholder={t('command list input placeholder')} />
|
|
76
|
+
<SearchList.Content classNames={cardDialogPaddedOverflow}>
|
|
77
|
+
{actions?.map((action) => {
|
|
78
|
+
const label = toLocalizedString(action.properties.label, t);
|
|
79
|
+
const shortcut =
|
|
80
|
+
typeof action.properties.keyBinding === 'string'
|
|
81
|
+
? action.properties.keyBinding
|
|
82
|
+
: action.properties.keyBinding?.[getHostPlatform()];
|
|
83
|
+
return (
|
|
84
|
+
<SearchList.Item
|
|
85
|
+
value={label}
|
|
86
|
+
key={action.id}
|
|
87
|
+
onSelect={() => {
|
|
88
|
+
if (action.properties.disabled) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
}}
|
|
98
|
-
classNames='flex items-center gap-2'
|
|
99
|
-
disabled={action.properties.disabled}
|
|
100
|
-
{...(action.properties?.testId && { 'data-testid': action.properties.testId })}
|
|
101
|
-
>
|
|
102
|
-
<Icon icon={action.properties.icon} size={4} />
|
|
103
|
-
<span className='grow truncate'>{label}</span>
|
|
104
|
-
{shortcut && <span className={mx('shrink-0', descriptionText)}>{keySymbols(shortcut).join('')}</span>}
|
|
105
|
-
</SearchList.Item>
|
|
106
|
-
);
|
|
107
|
-
})}
|
|
108
|
-
</SearchList.Content>
|
|
109
|
-
</SearchList.Root>
|
|
92
|
+
if (isActionGroup(action)) {
|
|
93
|
+
setSelected(action.id);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
97
|
+
void dispatch(
|
|
98
|
+
createIntent(LayoutAction.UpdateDialog, {
|
|
99
|
+
part: 'dialog',
|
|
100
|
+
options: { state: false },
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
const node = graph.getConnections(group?.id ?? action.id, 'inbound')[0];
|
|
105
|
+
void (node && isAction(action) && action.data({ parent: node, caller: KEY_BINDING }));
|
|
106
|
+
});
|
|
107
|
+
}}
|
|
108
|
+
classNames='flex items-center gap-2'
|
|
109
|
+
disabled={action.properties.disabled}
|
|
110
|
+
{...(action.properties?.testId && {
|
|
111
|
+
'data-testid': action.properties.testId,
|
|
112
|
+
})}
|
|
113
|
+
>
|
|
114
|
+
<Icon icon={action.properties.icon} size={4} />
|
|
115
|
+
<span className='grow truncate'>{label}</span>
|
|
116
|
+
{shortcut && <span className={mx('shrink-0', descriptionText)}>{keySymbols(shortcut).join('')}</span>}
|
|
117
|
+
</SearchList.Item>
|
|
118
|
+
);
|
|
119
|
+
})}
|
|
120
|
+
</SearchList.Content>
|
|
121
|
+
</SearchList.Root>
|
|
122
|
+
<div role='none' className='pli-cardSpacingInline pbe-cardSpacingBlock'>
|
|
123
|
+
<Dialog.Close asChild>
|
|
124
|
+
<Button classNames='is-full'>{t('close label', { ns: 'os' })}</Button>
|
|
125
|
+
</Dialog.Close>
|
|
126
|
+
</div>
|
|
127
|
+
</Dialog.Content>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
8
|
+
import { useIntentDispatcher } from '@dxos/app-framework/react';
|
|
8
9
|
import { Button, Icon, useTranslation } from '@dxos/react-ui';
|
|
9
10
|
|
|
10
|
-
import { COMMANDS_DIALOG,
|
|
11
|
+
import { COMMANDS_DIALOG, meta } from '../../meta';
|
|
11
12
|
|
|
12
13
|
// TODO(thure): Refactor to be handled by a more appropriate plugin.
|
|
13
14
|
export const CommandsTrigger = () => {
|
|
14
15
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
15
|
-
const { t } = useTranslation(
|
|
16
|
+
const { t } = useTranslation(meta.id);
|
|
16
17
|
return (
|
|
17
18
|
<Button
|
|
18
19
|
classNames='m-1 pli-1 lg:pli-2'
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type
|
|
8
|
-
import {
|
|
9
|
-
import React from 'react';
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { type KeyboardEvent, useCallback, useRef } from 'react';
|
|
8
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
10
9
|
|
|
11
10
|
import {
|
|
12
11
|
Capabilities,
|
|
@@ -16,22 +15,20 @@ import {
|
|
|
16
15
|
contributes,
|
|
17
16
|
createResolver,
|
|
18
17
|
defineCapability,
|
|
19
|
-
useCapability,
|
|
20
18
|
} from '@dxos/app-framework';
|
|
19
|
+
import { useCapability } from '@dxos/app-framework/react';
|
|
21
20
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
22
21
|
import { live } from '@dxos/live-object';
|
|
23
22
|
import { AttentionPlugin } from '@dxos/plugin-attention';
|
|
24
|
-
import { ClientPlugin } from '@dxos/plugin-client';
|
|
25
23
|
import { GraphPlugin } from '@dxos/plugin-graph';
|
|
26
|
-
import { SpacePlugin } from '@dxos/plugin-space';
|
|
27
24
|
import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
|
|
28
25
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
29
26
|
import { faker } from '@dxos/random';
|
|
30
27
|
import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
|
|
31
|
-
import {
|
|
28
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
29
|
+
import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
32
30
|
import { Stack, StackItem } from '@dxos/react-ui-stack';
|
|
33
31
|
import { defaultTx, mx } from '@dxos/react-ui-theme';
|
|
34
|
-
import { withLayout } from '@dxos/storybook-utils';
|
|
35
32
|
|
|
36
33
|
import { NavTreePlugin } from '../../NavTreePlugin';
|
|
37
34
|
import { storybookGraphBuilders } from '../../testing';
|
|
@@ -68,6 +65,15 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
68
65
|
|
|
69
66
|
const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
70
67
|
const attentionAttrs = useAttentionAttributes(attendableId);
|
|
68
|
+
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
69
|
+
|
|
70
|
+
// NOTE(thure): This is the same workaround as in Plank, but that component is out of scope for this story.
|
|
71
|
+
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
72
|
+
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
73
|
+
if (event.target === event.currentTarget && event.key === 'Escape') {
|
|
74
|
+
rootElement.current?.closest('main')?.focus();
|
|
75
|
+
}
|
|
76
|
+
}, []);
|
|
71
77
|
|
|
72
78
|
return (
|
|
73
79
|
<StackItem.Root
|
|
@@ -75,10 +81,12 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
75
81
|
{...attentionAttrs}
|
|
76
82
|
classNames='bg-baseSurface border-ie border-separator'
|
|
77
83
|
size={30}
|
|
84
|
+
onKeyDown={handleKeyDown}
|
|
85
|
+
ref={rootElement}
|
|
78
86
|
>
|
|
79
87
|
<StoryPlankHeading attendableId={attendableId} />
|
|
80
88
|
<StackItem.Content toolbar>
|
|
81
|
-
<Toolbar.Root classNames='border-
|
|
89
|
+
<Toolbar.Root classNames='border-be border-subduedSeparator'>
|
|
82
90
|
<Toolbar.Button>Test</Toolbar.Button>
|
|
83
91
|
</Toolbar.Root>
|
|
84
92
|
|
|
@@ -116,20 +124,21 @@ const DefaultStory = () => {
|
|
|
116
124
|
);
|
|
117
125
|
};
|
|
118
126
|
|
|
119
|
-
const meta
|
|
127
|
+
const meta = {
|
|
120
128
|
title: 'plugins/plugin-navtree/NavTree',
|
|
121
129
|
component: NavTreeContainer,
|
|
122
130
|
render: DefaultStory,
|
|
123
131
|
decorators: [
|
|
132
|
+
withTheme,
|
|
124
133
|
withPluginManager({
|
|
125
134
|
plugins: [
|
|
126
135
|
ThemePlugin({ tx: defaultTx }),
|
|
127
|
-
StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
|
|
128
136
|
GraphPlugin(),
|
|
129
137
|
IntentPlugin(),
|
|
130
138
|
SettingsPlugin(),
|
|
131
139
|
AttentionPlugin(),
|
|
132
140
|
NavTreePlugin(),
|
|
141
|
+
StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
|
|
133
142
|
],
|
|
134
143
|
capabilities: (context) => [
|
|
135
144
|
contributes(StoryState, live({ tab: 'space-0' })),
|
|
@@ -147,54 +156,59 @@ const meta: Meta<typeof NavTreeContainer> = {
|
|
|
147
156
|
]),
|
|
148
157
|
],
|
|
149
158
|
}),
|
|
150
|
-
withLayout({ fullscreen: true }),
|
|
151
159
|
],
|
|
152
160
|
parameters: {
|
|
161
|
+
layout: 'fullscreen',
|
|
153
162
|
translations,
|
|
154
163
|
},
|
|
155
|
-
}
|
|
164
|
+
} satisfies Meta<typeof NavTreeContainer>;
|
|
156
165
|
|
|
157
166
|
export default meta;
|
|
158
167
|
|
|
159
168
|
type Story = StoryObj<typeof NavTreeContainer>;
|
|
160
169
|
|
|
161
|
-
export const Default: Story = {
|
|
170
|
+
export const Default: Story = {
|
|
171
|
+
play: async ({ canvasElement }) => {
|
|
172
|
+
const canvas = within(canvasElement);
|
|
162
173
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
174
|
+
// Find the element with treegrid role and click on its parent
|
|
175
|
+
const treegridElement = await canvas.findByRole('treegrid');
|
|
176
|
+
const treegridParent = treegridElement.parentElement;
|
|
177
|
+
if (treegridParent) {
|
|
178
|
+
await userEvent.click(treegridParent);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Press Escape
|
|
182
|
+
await userEvent.keyboard('{Escape}');
|
|
183
|
+
|
|
184
|
+
// Confirm that focus is on an element with attribute data-main-landmark="0"
|
|
185
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
|
|
186
|
+
|
|
187
|
+
// Press Tab
|
|
188
|
+
await userEvent.keyboard('{Tab}');
|
|
189
|
+
|
|
190
|
+
// Confirm that focus is now on an element with data-main-landmark="1"
|
|
191
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
|
|
192
|
+
|
|
193
|
+
// Press Tab
|
|
194
|
+
await userEvent.keyboard('{Tab}');
|
|
195
|
+
|
|
196
|
+
// Confirm that focus is now on an element with data-main-landmark="0"
|
|
197
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
|
|
198
|
+
|
|
199
|
+
// Press Shift-Tab
|
|
200
|
+
await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
|
|
201
|
+
|
|
202
|
+
// Press Enter
|
|
203
|
+
await userEvent.keyboard('{Enter}');
|
|
204
|
+
|
|
205
|
+
// Confirm that focus is now on an element with data-attendable-id="space-0:object-0"
|
|
206
|
+
await expect(document.activeElement).toHaveAttribute('data-attendable-id', 'space-0:object-0');
|
|
207
|
+
|
|
208
|
+
// Press Escape
|
|
209
|
+
await userEvent.keyboard('{Escape}');
|
|
210
|
+
|
|
211
|
+
// Confirm that focus is now on an element with data-main-landmark="1"
|
|
212
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
|
|
213
|
+
},
|
|
200
214
|
};
|
|
@@ -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';
|
|
@@ -16,7 +16,7 @@ export const NAV_TREE_ITEM = 'NavTreeItem';
|
|
|
16
16
|
|
|
17
17
|
export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'> & Pick<L1TabsProps, 'open'>;
|
|
18
18
|
|
|
19
|
-
export const NavTree = ({ id, root, ...props }
|
|
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
22
|
const topLevelCollections = useItems(root, { disposition: 'collection' });
|
|
@@ -47,7 +47,11 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
|
|
|
47
47
|
const path = useMemo(() => [id], [id]);
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
|
|
50
|
+
// TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
|
|
51
|
+
// it uses RovingFocus and doesn’t support moving focus to an item that is not a tab. Assess whether this situation
|
|
52
|
+
// should change including whether it should motivate a change in the design/taxonomy, or if this means this should
|
|
53
|
+
// not use `react-ui-tabs` at all.
|
|
54
|
+
<Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative' ref={forwardedRef}>
|
|
51
55
|
<L0Menu
|
|
52
56
|
menuActions={topLevelActions}
|
|
53
57
|
topLevelItems={l0Items}
|
|
@@ -59,4 +63,4 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
|
|
|
59
63
|
<L1Tabs topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
|
|
60
64
|
</Tabs.Root>
|
|
61
65
|
);
|
|
62
|
-
};
|
|
66
|
+
});
|