11import { AsideHeader , MenuItem as GravityMenuItem } from '@gravity-ui/navigation' ;
22import classNames from 'classnames' ;
3- import React , { ReactNode , useState } from 'react' ;
3+ import React , { ReactNode , useCallback , useState } from 'react' ;
44import { useDispatch , useSelector } from 'react-redux' ;
55import { matchPath , useLocation , useNavigate } from 'react-router-dom' ;
66
@@ -12,8 +12,10 @@ import {Footer} from './Footer';
1212import { EmptyReportCard } from '@/static/new-ui/components/Card/EmptyReportCard' ;
1313import { InfoPanel } from '@/static/new-ui/components/InfoPanel' ;
1414import { useAnalytics } from '@/static/new-ui/hooks/useAnalytics' ;
15+ import { useHotkey } from '@/static/new-ui/hooks/useHotkey' ;
1516import { setSectionSizes } from '../../../modules/actions/suites-page' ;
1617import { ArrowLeftToLine , ArrowRightFromLine } from '@gravity-ui/icons' ;
18+ import { Hotkey } from '@gravity-ui/uikit' ;
1719import { isSectionHidden } from '../../features/suites/utils' ;
1820import { Page , PathNames } from '@/constants' ;
1921
@@ -39,9 +41,15 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
3941 const location = useLocation ( ) ;
4042 const analytics = useAnalytics ( ) ;
4143
44+ const pageHotkeys : Record < string , string > = {
45+ [ PathNames . suites ] : 's' ,
46+ [ PathNames . visualChecks ] : 'v'
47+ } ;
48+
4249 const menuItems : GravityMenuItem [ ] = props . pages . map ( item => ( {
4350 id : item . url ,
4451 title : item . title ,
52+ tooltipText : < > { item . title } < Hotkey value = { pageHotkeys [ item . url ] } view = "dark" /> </ > ,
4553 icon : item . icon ,
4654 current : Boolean ( matchPath ( `${ item . url . replace ( / \/ $ / , '' ) } /*` , location . pathname ) ) ,
4755 onItemClick : ( ) : void => {
@@ -55,11 +63,13 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
5563 const backupSuitesPageSectionSizes = useSelector ( state => state . ui [ Page . suitesPage ] . backupSectionSizes ) ;
5664 if ( / \/ s u i t e s / . test ( location . pathname ) ) {
5765 const shouldExpandTree = isSectionHidden ( currentSuitesPageSectionSizes [ 0 ] ) ;
66+ const treeTitle = shouldExpandTree ? 'Expand tree' : 'Collapse tree' ;
5867 menuItems . push (
5968 { id : 'divider' , type : 'divider' , title : '-' } ,
6069 {
6170 id : 'expand-collapse-tree' ,
62- title : shouldExpandTree ? 'Expand tree' : 'Collapse tree' ,
71+ title : treeTitle ,
72+ tooltipText : < > { treeTitle } < Hotkey value = "t" view = "dark" /> </ > ,
6373 icon : shouldExpandTree ? ArrowRightFromLine : ArrowLeftToLine ,
6474 onItemClick : ( ) : void => {
6575 dispatch ( setSectionSizes ( { sizes : shouldExpandTree ? backupSuitesPageSectionSizes : [ 0 , 100 ] , page : Page . suitesPage } ) ) ;
@@ -73,11 +83,13 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
7383 const backupVisualChecksPageSectionSizes = useSelector ( state => state . ui [ Page . visualChecksPage ] . backupSectionSizes ) ;
7484 if ( / \/ v i s u a l - c h e c k s / . test ( location . pathname ) ) {
7585 const shouldExpandTree = isSectionHidden ( currentVisualChecksPageSectionSizes [ 0 ] ) ;
86+ const treeTitle = shouldExpandTree ? 'Expand tree' : 'Collapse tree' ;
7687 menuItems . push (
7788 { id : 'divider' , type : 'divider' , title : '-' } ,
7889 {
7990 id : 'expand-collapse-tree' ,
80- title : shouldExpandTree ? 'Expand tree' : 'Collapse tree' ,
91+ title : treeTitle ,
92+ tooltipText : < > { treeTitle } < Hotkey value = "t" view = "dark" /> </ > ,
8193 icon : shouldExpandTree ? ArrowRightFromLine : ArrowLeftToLine ,
8294 onItemClick : ( ) : void => {
8395 dispatch ( setSectionSizes ( { sizes : shouldExpandTree ? backupVisualChecksPageSectionSizes : [ 0 , 100 ] , page : Page . visualChecksPage } ) ) ;
@@ -102,6 +114,34 @@ export function MainLayout(props: MainLayoutProps): ReactNode {
102114 }
103115 } ;
104116
117+ const togglePanel = useCallback ( ( panelId : PanelId ) : void => {
118+ setVisiblePanel ( prev => prev === panelId ? null : panelId ) ;
119+ } , [ ] ) ;
120+
121+ const toggleTreeSidebar = useCallback ( ( ) : void => {
122+ const isOnSuitesPage = / \/ s u i t e s / . test ( location . pathname ) ;
123+ const isOnVisualChecksPage = / \/ v i s u a l - c h e c k s / . test ( location . pathname ) ;
124+
125+ if ( isOnSuitesPage ) {
126+ const shouldExpand = isSectionHidden ( currentSuitesPageSectionSizes [ 0 ] ) ;
127+ dispatch ( setSectionSizes ( { sizes : shouldExpand ? backupSuitesPageSectionSizes : [ 0 , 100 ] , page : Page . suitesPage } ) ) ;
128+ } else if ( isOnVisualChecksPage ) {
129+ const shouldExpand = isSectionHidden ( currentVisualChecksPageSectionSizes [ 0 ] ) ;
130+ dispatch ( setSectionSizes ( { sizes : shouldExpand ? backupVisualChecksPageSectionSizes : [ 0 , 100 ] , page : Page . visualChecksPage } ) ) ;
131+ }
132+ } , [ location . pathname , currentSuitesPageSectionSizes , backupSuitesPageSectionSizes , currentVisualChecksPageSectionSizes , backupVisualChecksPageSectionSizes , dispatch ] ) ;
133+
134+ const navigateToSuites = useCallback ( ( ) => navigate ( PathNames . suites ) , [ navigate ] ) ;
135+ const navigateToVisualChecks = useCallback ( ( ) => navigate ( PathNames . visualChecks ) , [ navigate ] ) ;
136+ const toggleInfoPanel = useCallback ( ( ) => togglePanel ( PanelId . Info ) , [ togglePanel ] ) ;
137+ const toggleSettingsPanel = useCallback ( ( ) => togglePanel ( PanelId . Settings ) , [ togglePanel ] ) ;
138+
139+ useHotkey ( 's' , navigateToSuites ) ;
140+ useHotkey ( 'v' , navigateToVisualChecks ) ;
141+ useHotkey ( 't' , toggleTreeSidebar ) ;
142+ useHotkey ( 'i' , toggleInfoPanel ) ;
143+ useHotkey ( ',' , toggleSettingsPanel ) ;
144+
105145 return < AsideHeader
106146 className = { classNames ( { 'aside-header--initialized' : isInitialized } ) }
107147 logo = { { text : 'Testplane UI' , iconSrc : TestplaneIcon , iconSize : 32 , onClick : ( ) => navigate ( PathNames . suites ) } }
0 commit comments