From d4d8874a1b04879a588abd5a28bf5e7c0b39a77d Mon Sep 17 00:00:00 2001 From: "Roman.Sergeenko" Date: Fri, 26 Nov 2021 13:00:12 +0300 Subject: [PATCH 1/2] #RI-782 - display a page with summary of Shortcuts --- redisinsight/ui/src/App.tsx | 3 +- redisinsight/ui/src/components/index.ts | 4 +- .../keyboard-shortcut/KeyboardShortcut.tsx | 14 ++- .../keyboard-shortcut/styles.module.scss | 7 +- .../navigation-menu/NavigationMenu.tsx | 32 ++++-- .../navigation-menu/styles.module.scss | 21 ++-- .../shortcuts-flyout/ShortcutsFlyout.spec.tsx | 38 +++++++ .../shortcuts-flyout/ShortcutsFlyout.tsx | 70 ++++++++++++ .../components/shortcuts-flyout/schema.tsx | 42 +++++++ .../shortcuts-flyout/styles.module.scss | 38 +++++++ .../ui/src/constants/keyboardShortcuts.ts | 24 ---- .../ui/src/constants/keyboardShortcuts.tsx | 107 ++++++++++++++++++ redisinsight/ui/src/slices/app/info.ts | 7 +- redisinsight/ui/src/slices/interfaces/app.ts | 1 + .../ui/src/styles/components/_table.scss | 50 ++++---- 15 files changed, 383 insertions(+), 75 deletions(-) create mode 100644 redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.spec.tsx create mode 100644 redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.tsx create mode 100644 redisinsight/ui/src/components/shortcuts-flyout/schema.tsx create mode 100644 redisinsight/ui/src/components/shortcuts-flyout/styles.module.scss delete mode 100644 redisinsight/ui/src/constants/keyboardShortcuts.ts create mode 100644 redisinsight/ui/src/constants/keyboardShortcuts.tsx diff --git a/redisinsight/ui/src/App.tsx b/redisinsight/ui/src/App.tsx index 63cc50a085..61b458b345 100644 --- a/redisinsight/ui/src/App.tsx +++ b/redisinsight/ui/src/App.tsx @@ -7,7 +7,7 @@ import Router from './Router' import store from './slices/store' import { Theme } from './constants' import { themeService } from './services' -import { NavigationMenu, Notifications, Config } from './components' +import { NavigationMenu, Notifications, Config, ShortcutsFlyout } from './components' import { ThemeProvider } from './contexts/themeContext' import MainComponent from './components/main/MainComponent' @@ -32,6 +32,7 @@ const App = ({ children }: { children?: ReactElement }) => ( + diff --git a/redisinsight/ui/src/components/index.ts b/redisinsight/ui/src/components/index.ts index 966ed7f69a..d6697c14d0 100644 --- a/redisinsight/ui/src/components/index.ts +++ b/redisinsight/ui/src/components/index.ts @@ -13,6 +13,7 @@ import Config from './config' import AdvancedSettings from './advanced-settings/AdvancedSettings' import { ConsentsSettings, ConsentsSettingsPopup } from './consents-settings' import KeyboardShortcut from './keyboard-shortcut/KeyboardShortcut' +import ShortcutsFlyout from './shortcuts-flyout/ShortcutsFlyout' export { NavigationMenu, @@ -30,5 +31,6 @@ export { ConsentsSettings, ConsentsSettingsPopup, AdvancedSettings, - KeyboardShortcut + KeyboardShortcut, + ShortcutsFlyout } diff --git a/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx b/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx index 336a6c57ab..01de53c4a7 100644 --- a/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx +++ b/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx @@ -1,20 +1,22 @@ import React from 'react' +import cx from 'classnames' import { EuiBadge, EuiText } from '@elastic/eui' import styles from './styles.module.scss' export interface Props { - items: string[], - separator?: string + items: (string | JSX.Element)[]; + separator?: string; + transparent?: boolean; } -const KeyboardShortcut = ({ items = [], separator = '' }: Props) => ( +const KeyboardShortcut = ({ items = [], separator = '', transparent = false }: Props) => (
{ - items.map((item: string, index: number) => ( -
+ items.map((item: string | JSX.Element, index: number) => ( +
{ (index !== 0) &&
{separator}
} - + {item}
diff --git a/redisinsight/ui/src/components/keyboard-shortcut/styles.module.scss b/redisinsight/ui/src/components/keyboard-shortcut/styles.module.scss index cb840d9ad1..a53e4b8dc0 100644 --- a/redisinsight/ui/src/components/keyboard-shortcut/styles.module.scss +++ b/redisinsight/ui/src/components/keyboard-shortcut/styles.module.scss @@ -13,5 +13,10 @@ .badge { background-color: var(--euiTooltipBackgroundColor) !important; - border: 1px solid var(--euiToastSuccessBtnColor) !important;; + border: 1px solid var(--euiToastSuccessBtnColor) !important; +} + +.transparent { + background-color: transparent !important; + border-color: var(--separatorColor) !important; } diff --git a/redisinsight/ui/src/components/navigation-menu/NavigationMenu.tsx b/redisinsight/ui/src/components/navigation-menu/NavigationMenu.tsx index 0ce0091ed3..9f29e6112b 100644 --- a/redisinsight/ui/src/components/navigation-menu/NavigationMenu.tsx +++ b/redisinsight/ui/src/components/navigation-menu/NavigationMenu.tsx @@ -20,7 +20,7 @@ import { import { PageNames, Pages } from 'uiSrc/constants' import { getRouterLinkProps } from 'uiSrc/services' import { connectedInstanceSelector } from 'uiSrc/slices/instances' -import { setReleaseNotesViewed, appElectronInfoSelector } from 'uiSrc/slices/app/info' +import { setReleaseNotesViewed, appElectronInfoSelector, setShortcutsFlyoutState } from 'uiSrc/slices/app/info' import LogoSVG from 'uiSrc/assets/img/logo.svg' import SettingsSVG from 'uiSrc/assets/img/sidebar/settings.svg' import SettingsActiveSVG from 'uiSrc/assets/img/sidebar/settings_active.svg' @@ -71,6 +71,11 @@ const NavigationMenu = () => { history.push(Pages.browser(connectedInstanceId)) } + const onKeyboardShortcutClick = () => { + setIsHelpMenuActive(false) + dispatch(setShortcutsFlyoutState(true)) + } + const privateRoutes: INavigations[] = [ { tooltipText: 'Browser', @@ -177,16 +182,21 @@ const NavigationMenu = () => { - - - - - Keyboard Shortcuts - + onKeyboardShortcutClick()} + > +
+ + + + Keyboard Shortcuts + +
diff --git a/redisinsight/ui/src/components/navigation-menu/styles.module.scss b/redisinsight/ui/src/components/navigation-menu/styles.module.scss index 6667b83657..f2434fa588 100644 --- a/redisinsight/ui/src/components/navigation-menu/styles.module.scss +++ b/redisinsight/ui/src/components/navigation-menu/styles.module.scss @@ -103,23 +103,25 @@ $sideBarWidth: 60px; .helpMenuItem { align-items: center; + cursor: pointer; :global(.euiButtonIcon), :global(.euiIcon) { color: var(--euiTooltipTextColor) !important; } .helpMenuItemLink { - &:global(.euiLink) { - text-decoration: none !important; - display: flex; - flex-direction: column; - align-items: center; - transition: transform 0.3s ease; + text-decoration: none !important; + display: flex; + flex-direction: column; + align-items: center; + transition: transform 0.3s ease; - &:hover { - transform: translateY(-1px); - } + &:hover { + transform: translateY(-1px); + } + &:global(.euiLink) { + text-decoration: none !important; &:focus { animation: none !important; } @@ -128,6 +130,7 @@ $sideBarWidth: 60px; } .helpMenuItemDisabled { + cursor: auto; :global(.euiIcon), div { color: var(--buttonSecondaryDisabledTextColor) !important; } diff --git a/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.spec.tsx b/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.spec.tsx new file mode 100644 index 0000000000..b6f266de1b --- /dev/null +++ b/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.spec.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { cloneDeep } from 'lodash' +import { cleanup, mockedStore, render } from 'uiSrc/utils/test-utils' +import ShortcutsFlyout from './ShortcutsFlyout' +import { SHORTCUTS, ShortcutGroup } from './schema' + +let store: typeof mockedStore +beforeEach(() => { + cleanup() + store = cloneDeep(mockedStore) + store.clearActions() +}) + +const appInfoSlicesPath = 'uiSrc/slices/app/info' + +jest.mock(appInfoSlicesPath, () => ({ + ...jest.requireActual(appInfoSlicesPath), + appInfoSelector: jest.fn().mockReturnValue({ + ...jest.requireActual(appInfoSlicesPath).appInfoSelector, + isShortcutsFlyoutOpen: true + }), +})) + +describe('ShortcutsFlyout', () => { + it('should render', () => { + expect(render()).toBeTruthy() + }) + + it('should render groups', () => { + render() + + SHORTCUTS.forEach((group: ShortcutGroup) => { + expect( + document.querySelector(`[data-test-subj="shortcut-title-${group.name}"]`) + ).toBeInTheDocument() + }) + }) +}) diff --git a/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.tsx b/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.tsx new file mode 100644 index 0000000000..3e78d06eb8 --- /dev/null +++ b/redisinsight/ui/src/components/shortcuts-flyout/ShortcutsFlyout.tsx @@ -0,0 +1,70 @@ +import React from 'react' +import cx from 'classnames' +import { useDispatch, useSelector } from 'react-redux' +import { + EuiBasicTableColumn, + EuiFlyout, + EuiFlyoutBody, + EuiInMemoryTable, + EuiSpacer, + EuiTitle +} from '@elastic/eui' +import { appInfoSelector, setShortcutsFlyoutState } from 'uiSrc/slices/app/info' +import { KeyboardShortcut } from 'uiSrc/components' +import { SHORTCUTS, ShortcutGroup, separator } from './schema' + +import styles from './styles.module.scss' + +const ShortcutsFlyout = () => { + const { isShortcutsFlyoutOpen } = useSelector(appInfoSelector) + + const dispatch = useDispatch() + + const tableColumns: EuiBasicTableColumn[] = [ + { + name: '', + field: 'description', + width: '60%' + }, + { + name: '', + field: 'keys', + width: '40%', + render: (items: string[]) => + } + ] + + const ShortcutsTable = ({ name, items }: ShortcutGroup) => ( +
+ +
{name}
+
+ + + +
+ ) + + return isShortcutsFlyoutOpen ? ( + dispatch(setShortcutsFlyoutState(false))} + > + + +

Shortcuts

+
+ + {SHORTCUTS.map(ShortcutsTable)} +
+
+ ) : null +} + +export default ShortcutsFlyout diff --git a/redisinsight/ui/src/components/shortcuts-flyout/schema.tsx b/redisinsight/ui/src/components/shortcuts-flyout/schema.tsx new file mode 100644 index 0000000000..4219ed43c5 --- /dev/null +++ b/redisinsight/ui/src/components/shortcuts-flyout/schema.tsx @@ -0,0 +1,42 @@ +import { KEYBOARD_SHORTCUTS } from 'uiSrc/constants' + +export interface Shortcut { + label?: string + description: string + keys: (string | JSX.Element)[] +} + +export interface ShortcutGroup { + name: string + items: Shortcut[] +} + +export const separator = KEYBOARD_SHORTCUTS._separator + +export const SHORTCUTS: ShortcutGroup[] = [ + { + name: 'Desktop application', + items: [ + KEYBOARD_SHORTCUTS.desktop.newWindow, + KEYBOARD_SHORTCUTS.desktop.reloadPage, + ] + }, + { + name: 'CLI', + items: [ + KEYBOARD_SHORTCUTS.cli.autocompleteNext, + KEYBOARD_SHORTCUTS.cli.autocompletePrev, + KEYBOARD_SHORTCUTS.cli.clearSearch, + KEYBOARD_SHORTCUTS.cli.prevCommand, + KEYBOARD_SHORTCUTS.cli.nextCommand, + ] + }, + { + name: 'Workbench', + items: [ + KEYBOARD_SHORTCUTS.workbench.runQuery, + KEYBOARD_SHORTCUTS.workbench.nextLine, + KEYBOARD_SHORTCUTS.workbench.listOfCommands + ] + }, +] diff --git a/redisinsight/ui/src/components/shortcuts-flyout/styles.module.scss b/redisinsight/ui/src/components/shortcuts-flyout/styles.module.scss new file mode 100644 index 0000000000..070105d873 --- /dev/null +++ b/redisinsight/ui/src/components/shortcuts-flyout/styles.module.scss @@ -0,0 +1,38 @@ +.title { + font-size: 18px; + font-weight: 600 !important; +} +.table { + :global(thead) { + display: none; + } + &:global(.inMemoryTableDefault .euiTableCellContent span) { + padding-top: 0 !important; + white-space: normal; + + } + :global(.euiBadge) { + height: 22px; + min-width: 34px !important; + display: flex; + justify-content: center; + align-items: center; + + :global(.euiText) { + font-weight: 500; + } + + :global(.badgeArrowUp), :global(.badgeArrowDown), :global(.shiftSymbol) { + position: relative; + top: -3px + } + + :global(.shiftSymbol) { + top: -2px + } + + :global(.cmdSymbol) { + font-size: 12px; + } + } +} diff --git a/redisinsight/ui/src/constants/keyboardShortcuts.ts b/redisinsight/ui/src/constants/keyboardShortcuts.ts deleted file mode 100644 index deed27fae0..0000000000 --- a/redisinsight/ui/src/constants/keyboardShortcuts.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { isMacOs } from 'uiSrc/utils/handlePlatforms' - -// TODO: use i18n file for labels -const COMMON_SHORTCUTS = { - _separator: '+', - workbench: { - runQuery: { - label: 'Run', - keys: ['Ctrl', 'Enter'], - }, - } -} - -const MAC_SHORTCUTS = { - _separator: '', - workbench: { - runQuery: { - label: 'Run', - keys: ['⌘', 'Enter'], - }, - } -} - -export const KEYBOARD_SHORTCUTS = isMacOs() ? MAC_SHORTCUTS : COMMON_SHORTCUTS diff --git a/redisinsight/ui/src/constants/keyboardShortcuts.tsx b/redisinsight/ui/src/constants/keyboardShortcuts.tsx new file mode 100644 index 0000000000..2e80e9a7d6 --- /dev/null +++ b/redisinsight/ui/src/constants/keyboardShortcuts.tsx @@ -0,0 +1,107 @@ +import React from 'react' +import { isMacOs } from 'uiSrc/utils/handlePlatforms' + +// TODO: use i18n file for labels & descriptions +const COMMON_SHORTCUTS = { + _separator: '+', + desktop: { + newWindow: { + description: 'Open a new window', + keys: ['Ctrl', 'N'] + }, + reloadPage: { + description: 'Reload the page', + keys: ['Ctrl', 'R'] + } + }, + cli: { + autocompleteNext: { + description: 'Autocomplete with the next command', + keys: ['Tab'] + }, + autocompletePrev: { + description: 'Autocomplete with the previous command', + keys: ['Shift', 'Tab'] + }, + clearSearch: { + description: 'Clear the screen', + keys: ['Ctrl', 'L'] + }, + prevCommand: { + description: 'Return to the previous command', + keys: ['Up Arrow'] + }, + nextCommand: { + description: 'Scroll the list of commands in the opposite direction to the Up Arrow', + keys: ['Down Arrow'] + } + }, + workbench: { + runQuery: { + label: 'Run', + description: 'Run Command', + keys: ['Ctrl', 'Enter'], + }, + nextLine: { + description: 'Go to the next line', + keys: ['Enter'], + }, + listOfCommands: { + description: 'Display the list of commands and information about commands and their arguments in the suggestion list', + keys: ['Ctrl', 'Space'] + } + } +} + +const MAC_SHORTCUTS = { + _separator: '', + desktop: { + newWindow: { + description: 'Open a new window', + keys: [(), 'N'] + }, + reloadPage: { + description: 'Reload the page', + keys: [(), 'R'] + } + }, + cli: { + autocompleteNext: { + description: 'Autocomplete with the next command', + keys: ['Tab'] + }, + autocompletePrev: { + description: 'Autocomplete with the previous command', + keys: [(), 'Tab'] + }, + clearSearch: { + description: 'Clear the screen', + keys: [(), 'L'] + }, + prevCommand: { + description: 'Return to the previous command', + keys: [()] + }, + nextCommand: { + description: 'Scroll the list of commands in the opposite direction to the Up Arrow', + keys: [()] + } + }, + workbench: { + runQuery: { + label: 'Run', + description: 'Run Command', + keys: [(), 'Enter'], + }, + nextLine: { + description: 'Go to the next line', + keys: ['Enter'], + }, + listOfCommands: { + description: 'Display the list of commands and information about commands and their arguments in the suggestion list', + keys: [(), 'Space'] + } + } +} + +export const KEYBOARD_SHORTCUTS = isMacOs() ? MAC_SHORTCUTS : COMMON_SHORTCUTS diff --git a/redisinsight/ui/src/slices/app/info.ts b/redisinsight/ui/src/slices/app/info.ts index ea4865536e..9e81320d3d 100644 --- a/redisinsight/ui/src/slices/app/info.ts +++ b/redisinsight/ui/src/slices/app/info.ts @@ -21,7 +21,8 @@ export const initialState: StateAppInfo = { isUpdateAvailable: null, updateDownloadedVersion: '', isReleaseNotesViewed: null, - } + }, + isShortcutsFlyoutOpen: false } // A slice for recipes @@ -51,6 +52,9 @@ const appInfoSlice = createSlice({ state.loading = false state.error = payload }, + setShortcutsFlyoutState: (state, { payload }) => { + state.isShortcutsFlyoutOpen = payload + } }, }) @@ -63,6 +67,7 @@ export const { getServerInfo, getServerInfoSuccess, getServerInfoFailure, + setShortcutsFlyoutState } = appInfoSlice.actions // A selector diff --git a/redisinsight/ui/src/slices/interfaces/app.ts b/redisinsight/ui/src/slices/interfaces/app.ts index cba9d75423..9ff5afeb6a 100644 --- a/redisinsight/ui/src/slices/interfaces/app.ts +++ b/redisinsight/ui/src/slices/interfaces/app.ts @@ -28,6 +28,7 @@ export interface StateAppInfo { updateDownloadedVersion: string; isReleaseNotesViewed: Nullable; }; + isShortcutsFlyoutOpen: boolean; } export interface StateAppContext { diff --git a/redisinsight/ui/src/styles/components/_table.scss b/redisinsight/ui/src/styles/components/_table.scss index 46a301bf4a..b941397f19 100644 --- a/redisinsight/ui/src/styles/components/_table.scss +++ b/redisinsight/ui/src/styles/components/_table.scss @@ -1,37 +1,45 @@ table .euiTableHeaderCellCheckbox { width: 44px; } -table .euiTableRow { - td { - &:first-child { - border-left: 1px solid var(--euiColorLightShade); - @media only screen and (max-width: 767px) { - border-left: none; - } - } - &:last-child { - border-right: 1px solid var(--euiColorLightShade); +table { + &.euiTable--responsive .euiTableRow { + td { @media only screen and (max-width: 767px) { - border-right: none; + &:first-child { + border-left: none; + } + &:last-child { + border-right: none; + } } } } - &-isSelected { - border-color: var(--euiColorPrimary) !important; - background: var(--tableRowSelectedColor) !important; + .euiTableRow { td { &:first-child { - border-left-color: var(--euiColorPrimary); + border-left: 1px solid var(--euiColorLightShade); } &:last-child { - border-right-color: var(--euiColorPrimary); + border-right: 1px solid var(--euiColorLightShade); } - border-top: 1px solid var(--euiColorPrimary); - border-bottom: 1px solid var(--euiColorPrimary) !important; } - } - &:hover { - background-color: var(--tableRowHoverColor); + &-isSelected { + border-color: var(--euiColorPrimary) !important; + background: var(--tableRowSelectedColor) !important; + td { + &:first-child { + border-left-color: var(--euiColorPrimary); + } + &:last-child { + border-right-color: var(--euiColorPrimary); + } + border-top: 1px solid var(--euiColorPrimary); + border-bottom: 1px solid var(--euiColorPrimary) !important; + } + } + &:hover { + background-color: var(--tableRowHoverColor); + } } } From 258c2517bc06b24bffa42481a72432a1515fd26e Mon Sep 17 00:00:00 2001 From: "Roman.Sergeenko" Date: Fri, 26 Nov 2021 13:14:35 +0300 Subject: [PATCH 2/2] #RI-782 - fix pr comments --- .../ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx b/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx index 01de53c4a7..f776cd722c 100644 --- a/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx +++ b/redisinsight/ui/src/components/keyboard-shortcut/KeyboardShortcut.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { isString } from 'lodash' import cx from 'classnames' import { EuiBadge, EuiText } from '@elastic/eui' @@ -14,7 +15,7 @@ const KeyboardShortcut = ({ items = [], separator = '', transparent = false }: P
{ items.map((item: string | JSX.Element, index: number) => ( -
+
{ (index !== 0) &&
{separator}
} {item}