Skip to content

Commit d4d8874

Browse files
author
Roman.Sergeenko
committed
#RI-782 - display a page with summary of Shortcuts
1 parent 1bee468 commit d4d8874

File tree

15 files changed

+383
-75
lines changed

15 files changed

+383
-75
lines changed

redisinsight/ui/src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Router from './Router'
77
import store from './slices/store'
88
import { Theme } from './constants'
99
import { themeService } from './services'
10-
import { NavigationMenu, Notifications, Config } from './components'
10+
import { NavigationMenu, Notifications, Config, ShortcutsFlyout } from './components'
1111
import { ThemeProvider } from './contexts/themeContext'
1212
import MainComponent from './components/main/MainComponent'
1313

@@ -32,6 +32,7 @@ const App = ({ children }: { children?: ReactElement }) => (
3232
</EuiPageBody>
3333
</EuiPage>
3434
<Notifications />
35+
<ShortcutsFlyout />
3536
</div>
3637
</Router>
3738
</ThemeProvider>

redisinsight/ui/src/components/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Config from './config'
1313
import AdvancedSettings from './advanced-settings/AdvancedSettings'
1414
import { ConsentsSettings, ConsentsSettingsPopup } from './consents-settings'
1515
import KeyboardShortcut from './keyboard-shortcut/KeyboardShortcut'
16+
import ShortcutsFlyout from './shortcuts-flyout/ShortcutsFlyout'
1617

1718
export {
1819
NavigationMenu,
@@ -30,5 +31,6 @@ export {
3031
ConsentsSettings,
3132
ConsentsSettingsPopup,
3233
AdvancedSettings,
33-
KeyboardShortcut
34+
KeyboardShortcut,
35+
ShortcutsFlyout
3436
}

redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import React from 'react'
2+
import cx from 'classnames'
23
import { EuiBadge, EuiText } from '@elastic/eui'
34

45
import styles from './styles.module.scss'
56

67
export interface Props {
7-
items: string[],
8-
separator?: string
8+
items: (string | JSX.Element)[];
9+
separator?: string;
10+
transparent?: boolean;
911
}
1012

11-
const KeyboardShortcut = ({ items = [], separator = '' }: Props) => (
13+
const KeyboardShortcut = ({ items = [], separator = '', transparent = false }: Props) => (
1214
<div className={styles.container}>
1315
{
14-
items.map((item: string, index: number) => (
15-
<div key={item}>
16+
items.map((item: string | JSX.Element, index: number) => (
17+
<div key={typeof item === 'string' ? item : item?.props?.children}>
1618
{ (index !== 0) && <div className={styles.separator}>{separator}</div> }
17-
<EuiBadge className={styles.badge}>
19+
<EuiBadge className={cx(styles.badge, { [styles.transparent]: transparent })}>
1820
<EuiText size="s">{item}</EuiText>
1921
</EuiBadge>
2022
</div>

redisinsight/ui/src/components/keyboard-shortcut/styles.module.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@
1313

1414
.badge {
1515
background-color: var(--euiTooltipBackgroundColor) !important;
16-
border: 1px solid var(--euiToastSuccessBtnColor) !important;;
16+
border: 1px solid var(--euiToastSuccessBtnColor) !important;
17+
}
18+
19+
.transparent {
20+
background-color: transparent !important;
21+
border-color: var(--separatorColor) !important;
1722
}

redisinsight/ui/src/components/navigation-menu/NavigationMenu.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { PageNames, Pages } from 'uiSrc/constants'
2121
import { getRouterLinkProps } from 'uiSrc/services'
2222
import { connectedInstanceSelector } from 'uiSrc/slices/instances'
23-
import { setReleaseNotesViewed, appElectronInfoSelector } from 'uiSrc/slices/app/info'
23+
import { setReleaseNotesViewed, appElectronInfoSelector, setShortcutsFlyoutState } from 'uiSrc/slices/app/info'
2424
import LogoSVG from 'uiSrc/assets/img/logo.svg'
2525
import SettingsSVG from 'uiSrc/assets/img/sidebar/settings.svg'
2626
import SettingsActiveSVG from 'uiSrc/assets/img/sidebar/settings_active.svg'
@@ -71,6 +71,11 @@ const NavigationMenu = () => {
7171
history.push(Pages.browser(connectedInstanceId))
7272
}
7373

74+
const onKeyboardShortcutClick = () => {
75+
setIsHelpMenuActive(false)
76+
dispatch(setShortcutsFlyoutState(true))
77+
}
78+
7479
const privateRoutes: INavigations[] = [
7580
{
7681
tooltipText: 'Browser',
@@ -177,16 +182,21 @@ const NavigationMenu = () => {
177182
</EuiLink>
178183
</EuiFlexItem>
179184

180-
<EuiFlexItem className={cx(styles.helpMenuItem, styles.helpMenuItemDisabled)}>
181-
<EuiIcon type="keyboardShortcut" size="xl" />
182-
<EuiSpacer size="s" />
183-
<EuiText
184-
size="xs"
185-
textAlign="center"
186-
className={styles.helpMenuText}
187-
>
188-
Keyboard Shortcuts
189-
</EuiText>
185+
<EuiFlexItem
186+
className={styles.helpMenuItem}
187+
onClick={() => onKeyboardShortcutClick()}
188+
>
189+
<div className={styles.helpMenuItemLink}>
190+
<EuiIcon type="keyboardShortcut" size="xl" />
191+
<EuiSpacer size="s" />
192+
<EuiText
193+
size="xs"
194+
textAlign="center"
195+
className={styles.helpMenuText}
196+
>
197+
Keyboard Shortcuts
198+
</EuiText>
199+
</div>
190200
</EuiFlexItem>
191201

192202
<EuiFlexItem className={styles.helpMenuItem}>

redisinsight/ui/src/components/navigation-menu/styles.module.scss

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,25 @@ $sideBarWidth: 60px;
103103

104104
.helpMenuItem {
105105
align-items: center;
106+
cursor: pointer;
106107

107108
:global(.euiButtonIcon), :global(.euiIcon) {
108109
color: var(--euiTooltipTextColor) !important;
109110
}
110111

111112
.helpMenuItemLink {
112-
&:global(.euiLink) {
113-
text-decoration: none !important;
114-
display: flex;
115-
flex-direction: column;
116-
align-items: center;
117-
transition: transform 0.3s ease;
113+
text-decoration: none !important;
114+
display: flex;
115+
flex-direction: column;
116+
align-items: center;
117+
transition: transform 0.3s ease;
118118

119-
&:hover {
120-
transform: translateY(-1px);
121-
}
119+
&:hover {
120+
transform: translateY(-1px);
121+
}
122122

123+
&:global(.euiLink) {
124+
text-decoration: none !important;
123125
&:focus {
124126
animation: none !important;
125127
}
@@ -128,6 +130,7 @@ $sideBarWidth: 60px;
128130
}
129131

130132
.helpMenuItemDisabled {
133+
cursor: auto;
131134
:global(.euiIcon), div {
132135
color: var(--buttonSecondaryDisabledTextColor) !important;
133136
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react'
2+
import { cloneDeep } from 'lodash'
3+
import { cleanup, mockedStore, render } from 'uiSrc/utils/test-utils'
4+
import ShortcutsFlyout from './ShortcutsFlyout'
5+
import { SHORTCUTS, ShortcutGroup } from './schema'
6+
7+
let store: typeof mockedStore
8+
beforeEach(() => {
9+
cleanup()
10+
store = cloneDeep(mockedStore)
11+
store.clearActions()
12+
})
13+
14+
const appInfoSlicesPath = 'uiSrc/slices/app/info'
15+
16+
jest.mock(appInfoSlicesPath, () => ({
17+
...jest.requireActual(appInfoSlicesPath),
18+
appInfoSelector: jest.fn().mockReturnValue({
19+
...jest.requireActual(appInfoSlicesPath).appInfoSelector,
20+
isShortcutsFlyoutOpen: true
21+
}),
22+
}))
23+
24+
describe('ShortcutsFlyout', () => {
25+
it('should render', () => {
26+
expect(render(<ShortcutsFlyout />)).toBeTruthy()
27+
})
28+
29+
it('should render groups', () => {
30+
render(<ShortcutsFlyout />)
31+
32+
SHORTCUTS.forEach((group: ShortcutGroup) => {
33+
expect(
34+
document.querySelector(`[data-test-subj="shortcut-title-${group.name}"]`)
35+
).toBeInTheDocument()
36+
})
37+
})
38+
})
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react'
2+
import cx from 'classnames'
3+
import { useDispatch, useSelector } from 'react-redux'
4+
import {
5+
EuiBasicTableColumn,
6+
EuiFlyout,
7+
EuiFlyoutBody,
8+
EuiInMemoryTable,
9+
EuiSpacer,
10+
EuiTitle
11+
} from '@elastic/eui'
12+
import { appInfoSelector, setShortcutsFlyoutState } from 'uiSrc/slices/app/info'
13+
import { KeyboardShortcut } from 'uiSrc/components'
14+
import { SHORTCUTS, ShortcutGroup, separator } from './schema'
15+
16+
import styles from './styles.module.scss'
17+
18+
const ShortcutsFlyout = () => {
19+
const { isShortcutsFlyoutOpen } = useSelector(appInfoSelector)
20+
21+
const dispatch = useDispatch()
22+
23+
const tableColumns: EuiBasicTableColumn<any>[] = [
24+
{
25+
name: '',
26+
field: 'description',
27+
width: '60%'
28+
},
29+
{
30+
name: '',
31+
field: 'keys',
32+
width: '40%',
33+
render: (items: string[]) => <KeyboardShortcut items={items} separator={separator} transparent />
34+
}
35+
]
36+
37+
const ShortcutsTable = ({ name, items }: ShortcutGroup) => (
38+
<div key={name}>
39+
<EuiTitle size="xxs" data-test-subj={`shortcut-title-${name}`}>
40+
<h6>{name}</h6>
41+
</EuiTitle>
42+
<EuiSpacer size="m" />
43+
<EuiInMemoryTable
44+
className={cx('inMemoryTableDefault', styles.table)}
45+
columns={tableColumns}
46+
items={items}
47+
responsive={false}
48+
/>
49+
<EuiSpacer size="xl" />
50+
</div>
51+
)
52+
53+
return isShortcutsFlyoutOpen ? (
54+
<EuiFlyout
55+
ownFocus
56+
size="538px"
57+
onClose={() => dispatch(setShortcutsFlyoutState(false))}
58+
>
59+
<EuiFlyoutBody>
60+
<EuiTitle size="s" className={styles.title}>
61+
<h4>Shortcuts</h4>
62+
</EuiTitle>
63+
<EuiSpacer size="m" />
64+
{SHORTCUTS.map(ShortcutsTable)}
65+
</EuiFlyoutBody>
66+
</EuiFlyout>
67+
) : null
68+
}
69+
70+
export default ShortcutsFlyout
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { KEYBOARD_SHORTCUTS } from 'uiSrc/constants'
2+
3+
export interface Shortcut {
4+
label?: string
5+
description: string
6+
keys: (string | JSX.Element)[]
7+
}
8+
9+
export interface ShortcutGroup {
10+
name: string
11+
items: Shortcut[]
12+
}
13+
14+
export const separator = KEYBOARD_SHORTCUTS._separator
15+
16+
export const SHORTCUTS: ShortcutGroup[] = [
17+
{
18+
name: 'Desktop application',
19+
items: [
20+
KEYBOARD_SHORTCUTS.desktop.newWindow,
21+
KEYBOARD_SHORTCUTS.desktop.reloadPage,
22+
]
23+
},
24+
{
25+
name: 'CLI',
26+
items: [
27+
KEYBOARD_SHORTCUTS.cli.autocompleteNext,
28+
KEYBOARD_SHORTCUTS.cli.autocompletePrev,
29+
KEYBOARD_SHORTCUTS.cli.clearSearch,
30+
KEYBOARD_SHORTCUTS.cli.prevCommand,
31+
KEYBOARD_SHORTCUTS.cli.nextCommand,
32+
]
33+
},
34+
{
35+
name: 'Workbench',
36+
items: [
37+
KEYBOARD_SHORTCUTS.workbench.runQuery,
38+
KEYBOARD_SHORTCUTS.workbench.nextLine,
39+
KEYBOARD_SHORTCUTS.workbench.listOfCommands
40+
]
41+
},
42+
]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.title {
2+
font-size: 18px;
3+
font-weight: 600 !important;
4+
}
5+
.table {
6+
:global(thead) {
7+
display: none;
8+
}
9+
&:global(.inMemoryTableDefault .euiTableCellContent span) {
10+
padding-top: 0 !important;
11+
white-space: normal;
12+
13+
}
14+
:global(.euiBadge) {
15+
height: 22px;
16+
min-width: 34px !important;
17+
display: flex;
18+
justify-content: center;
19+
align-items: center;
20+
21+
:global(.euiText) {
22+
font-weight: 500;
23+
}
24+
25+
:global(.badgeArrowUp), :global(.badgeArrowDown), :global(.shiftSymbol) {
26+
position: relative;
27+
top: -3px
28+
}
29+
30+
:global(.shiftSymbol) {
31+
top: -2px
32+
}
33+
34+
:global(.cmdSymbol) {
35+
font-size: 12px;
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)