Skip to content

Commit ca47b17

Browse files
committed
feat: add hotkeys panel to the menu
1 parent 16342ac commit ca47b17

File tree

5 files changed

+135
-1
lines changed

5 files changed

+135
-1
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.hotkeys-panel {
2+
height: 100%;
3+
}
4+
5+
.divider {
6+
margin: 16px 0;
7+
}
8+
9+
.hotkeys-list {
10+
display: flex;
11+
flex-direction: column;
12+
gap: 12px;
13+
margin-top: 12px;
14+
}
15+
16+
.hotkey-item {
17+
display: flex;
18+
justify-content: space-between;
19+
align-items: center;
20+
gap: 16px;
21+
}
22+
23+
.hotkey-title {
24+
color: var(--color-neutral-500);
25+
font-size: 15px;
26+
}
27+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {Divider, Hotkey} from '@gravity-ui/uikit';
2+
import React, {ReactNode} from 'react';
3+
4+
import {AsidePanel} from '@/static/new-ui/components/AsidePanel';
5+
import {PanelSection} from '@/static/new-ui/components/PanelSection';
6+
import {HOTKEYS_GROUPS} from '@/static/new-ui/components/MainLayout/hotkeys';
7+
import styles from './index.module.css';
8+
9+
export function HotkeysPanel(): ReactNode {
10+
const sections = HOTKEYS_GROUPS.map((group, groupIndex) => (
11+
<PanelSection key={groupIndex} title={group.title}>
12+
<div className={styles.hotkeysList}>
13+
{group.items.map((item, itemIndex) => (
14+
<div key={itemIndex} className={styles.hotkeyItem}>
15+
<span className={styles.hotkeyTitle}>{item.title}</span>
16+
<Hotkey value={item.value} view="light" />
17+
</div>
18+
))}
19+
</div>
20+
</PanelSection>
21+
));
22+
23+
const lastSection = sections.pop();
24+
25+
return (
26+
<AsidePanel title="Keyboard Shortcuts" className={styles.hotkeysPanel}>
27+
{sections.map((section, index) => (
28+
<React.Fragment key={index}>
29+
{section}
30+
<Divider orientation="horizontal" className={styles.divider} />
31+
</React.Fragment>
32+
))}
33+
{lastSection}
34+
</AsidePanel>
35+
);
36+
}
37+

lib/static/new-ui/components/MainLayout/Footer.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Gear, CircleInfo} from '@gravity-ui/icons';
1+
import {Gear, CircleInfo, Keyboard} from '@gravity-ui/icons';
22
import {FooterItem, MenuItem as GravityMenuItem} from '@gravity-ui/navigation';
33
import {Hotkey, Icon} from '@gravity-ui/uikit';
44
import classNames from 'classnames';
@@ -45,11 +45,28 @@ export function Footer(props: FooterProps): ReactNode {
4545
}
4646
}, [props.visiblePanel]);
4747

48+
const isHotkeysCurrent = props.visiblePanel === PanelId.Hotkeys;
4849
const isInfoCurrent = props.visiblePanel === PanelId.Info;
4950
const isSettingsCurrent = props.visiblePanel === PanelId.Settings;
5051

5152
return <>
5253
<UiModeHintNotification isVisible={isHintVisible} onClose={(): void => setIsHintVisible(false)} />
54+
<FooterItem compact={false} item={{
55+
id: PanelId.Hotkeys,
56+
title: 'Keyboard Shortcuts',
57+
tooltipText: <>Keyboard Shortcuts <Hotkey value="mod+/" view="dark" /></>,
58+
onItemClick: props.onFooterItemClick,
59+
current: isHotkeysCurrent,
60+
qa: 'footer-item-hotkeys',
61+
itemWrapper: (params, makeItem) => makeItem({
62+
...params,
63+
icon: <Icon className={classNames({
64+
[styles.footerItem]: !isHotkeysCurrent,
65+
[styles['footer-item--active']]: isHotkeysCurrent,
66+
disabled: !isInitialized
67+
})} data={Keyboard} />
68+
})
69+
}} />
5370
<FooterItem compact={false} item={{
5471
id: PanelId.Info,
5572
title: 'Info',
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type {HotkeysGroup} from '@gravity-ui/navigation';
2+
3+
export const HOTKEYS_GROUPS: HotkeysGroup[] = [
4+
{
5+
title: 'Navigation',
6+
items: [
7+
{title: 'Suites page', value: 's'},
8+
{title: 'Visual Checks page', value: 'v'},
9+
{title: 'Keyboard shortcuts', value: 'mod+/'},
10+
{title: 'Info panel', value: 'i'},
11+
{title: 'Settings panel', value: ','}
12+
]
13+
},
14+
{
15+
title: 'Tests Tree',
16+
items: [
17+
{title: 'Toggle tree sidebar', value: 't'},
18+
{title: 'Focus search', value: 'mod+k'},
19+
{title: 'Clear search', value: 'escape'}
20+
]
21+
},
22+
{
23+
title: 'Working with Tests',
24+
items: [
25+
{title: 'Previous test', value: '↑'},
26+
{title: 'Next test', value: '↓'},
27+
{title: 'Previous attempt', value: '←'},
28+
{title: 'Next attempt', value: '→'},
29+
{title: 'Run current test', value: 'r'},
30+
{title: 'Run all/selected tests', value: 'shift+r'},
31+
{title: 'Accept screenshot', value: 'a'},
32+
{title: 'Undo accept', value: 'u'},
33+
{title: 'Accept all/selected', value: 'shift+a'},
34+
{title: 'Go to Suites / Visual Checks', value: 'g'}
35+
]
36+
},
37+
{
38+
title: 'Time Travel Player',
39+
items: [
40+
{title: 'Show/hide player', value: 'p'},
41+
{title: 'Play/pause', value: 'k'}
42+
]
43+
}
44+
];
45+

lib/static/new-ui/components/MainLayout/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {matchPath, useLocation, useNavigate} from 'react-router-dom';
66

77
import {getIsInitialized} from '@/static/new-ui/store/selectors';
88
import {SettingsPanel} from '@/static/new-ui/components/SettingsPanel';
9+
import {HotkeysPanel} from '@/static/new-ui/components/HotkeysPanel';
910
import TestplaneIcon from '../../../icons/testplane-mono.svg';
1011
import styles from './index.module.css';
1112
import {Footer} from './Footer';
@@ -20,6 +21,7 @@ import {isSectionHidden} from '../../features/suites/utils';
2021
import {Page, PathNames} from '@/constants';
2122

2223
export enum PanelId {
24+
Hotkeys = 'hotkeys',
2325
Settings = 'settings',
2426
Info = 'info',
2527
}
@@ -133,12 +135,14 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
133135

134136
const navigateToSuites = useCallback(() => navigate(PathNames.suites), [navigate]);
135137
const navigateToVisualChecks = useCallback(() => navigate(PathNames.visualChecks), [navigate]);
138+
const toggleHotkeysPanel = useCallback(() => togglePanel(PanelId.Hotkeys), [togglePanel]);
136139
const toggleInfoPanel = useCallback(() => togglePanel(PanelId.Info), [togglePanel]);
137140
const toggleSettingsPanel = useCallback(() => togglePanel(PanelId.Settings), [togglePanel]);
138141

139142
useHotkey('s', navigateToSuites);
140143
useHotkey('v', navigateToVisualChecks);
141144
useHotkey('t', toggleTreeSidebar);
145+
useHotkey('mod+/', toggleHotkeysPanel);
142146
useHotkey('i', toggleInfoPanel);
143147
useHotkey(',', toggleSettingsPanel);
144148

@@ -160,6 +164,10 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
160164
hideCollapseButton={true}
161165
renderFooter={(): ReactNode => <Footer visiblePanel={visiblePanel} onFooterItemClick={onFooterItemClick}/>}
162166
panelItems={[{
167+
id: PanelId.Hotkeys,
168+
children: <HotkeysPanel />,
169+
visible: visiblePanel === PanelId.Hotkeys
170+
}, {
163171
id: PanelId.Info,
164172
children: <InfoPanel />,
165173
visible: visiblePanel === PanelId.Info

0 commit comments

Comments
 (0)