From a77c342764ba25bf92e0358d9232ac19340d6748 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Thu, 26 Jan 2023 12:07:32 +0000 Subject: [PATCH] Navigation: Add keyboard shortcut to search input (#62116) * Show shortcut * add icon to search bar, make same changes in search modal * rename to modKey Co-authored-by: joshhunt --- .../TopSearchBarCommandPaletteTrigger.tsx | 25 +++++++++++++++++-- public/app/core/components/help/HelpModal.tsx | 18 ++++++------- public/app/core/utils/browser.ts | 9 +++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx b/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx index db5b77eecff..c67660acaae 100644 --- a/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx +++ b/public/app/core/components/AppChrome/TopSearchBarCommandPaletteTrigger.tsx @@ -1,12 +1,13 @@ import { css } from '@emotion/css'; import { useKBar, VisualState } from 'kbar'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { getInputStyles, Icon, ToolbarButton, useStyles2, useTheme2 } from '@grafana/ui'; import { focusCss } from '@grafana/ui/src/themes/mixins'; import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange'; import { t } from 'app/core/internationalization'; +import { getModKey } from 'app/core/utils/browser'; export function TopSearchBarCommandPaletteTrigger() { const theme = useTheme2(); @@ -44,8 +45,13 @@ export function TopSearchBarCommandPaletteTrigger() { return ; } -function PretendTextInput({ onClick }: { onClick: () => void }) { +interface PretendTextInputProps { + onClick: () => void; +} + +function PretendTextInput({ onClick }: PretendTextInputProps) { const styles = useStyles2(getStyles); + const modKey = useMemo(() => getModKey(), []); // We want the desktop command palette trigger to look like a search box, // but it actually behaves like a button - you active it and it performs an @@ -61,6 +67,11 @@ function PretendTextInput({ onClick }: { onClick: () => void }) { + +
+ + {modKey}+k +
); @@ -73,6 +84,16 @@ const getStyles = (theme: GrafanaTheme2) => { wrapper: baseStyles.wrapper, inputWrapper: baseStyles.inputWrapper, prefix: baseStyles.prefix, + suffix: css([ + baseStyles.suffix, + { + display: 'flex', + gap: theme.spacing(0.5), + }, + ]), + shortcut: css({ + fontSize: theme.typography.bodySmall.fontSize, + }), fakeInput: css([ baseStyles.input, { diff --git a/public/app/core/components/help/HelpModal.tsx b/public/app/core/components/help/HelpModal.tsx index f51900e9571..3d9f0a5afa9 100644 --- a/public/app/core/components/help/HelpModal.tsx +++ b/public/app/core/components/help/HelpModal.tsx @@ -1,10 +1,11 @@ import { css } from '@emotion/css'; -import React from 'react'; +import React, { useMemo } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Modal, useStyles2 } from '@grafana/ui'; +import { getModKey } from 'app/core/utils/browser'; -const shortcuts = { +const getShortcuts = (modKey: string) => ({ Global: [ { keys: ['g', 'h'], description: 'Go to Home Dashboard' }, { keys: ['g', 'e'], description: 'Go to Explore' }, @@ -12,11 +13,11 @@ const shortcuts = { { keys: ['s', 'o'], description: 'Open search' }, { keys: ['esc'], description: 'Exit edit/setting views' }, { keys: ['h'], description: 'Show all keyboard shortcuts' }, - { keys: ['mod+k'], description: 'Open command palette' }, + { keys: [`${modKey}+k`], description: 'Open command palette' }, { keys: ['c', 't'], description: 'Change theme' }, ], Dashboard: [ - { keys: ['mod+s'], description: 'Save dashboard' }, + { keys: [`${modKey}+s`], description: 'Save dashboard' }, { keys: ['d', 'r'], description: 'Refresh all panels' }, { keys: ['d', 's'], description: 'Dashboard settings' }, { keys: ['d', 'v'], description: 'Toggle in-active / view mode' }, @@ -24,7 +25,7 @@ const shortcuts = { { keys: ['d', 'E'], description: 'Expand all rows' }, { keys: ['d', 'C'], description: 'Collapse all rows' }, { keys: ['d', 'a'], description: 'Toggle auto fit panels (experimental feature)' }, - { keys: ['mod+o'], description: 'Toggle shared graph crosshair' }, + { keys: [`${modKey}+o`], description: 'Toggle shared graph crosshair' }, { keys: ['d', 'l'], description: 'Toggle all panel legends' }, ], 'Focused Panel': [ @@ -50,7 +51,7 @@ const shortcuts = { description: 'Make time range absolute/permanent', }, ], -}; +}); export interface HelpModalProps { onDismiss: () => void; @@ -58,11 +59,10 @@ export interface HelpModalProps { export const HelpModal = ({ onDismiss }: HelpModalProps): JSX.Element => { const styles = useStyles2(getStyles); + const modKey = useMemo(() => getModKey(), []); + const shortcuts = useMemo(() => getShortcuts(modKey), [modKey]); return ( -
- mod = CTRL on windows or linux and CMD key on Mac -
{Object.entries(shortcuts).map(([category, shortcuts], i) => (
diff --git a/public/app/core/utils/browser.ts b/public/app/core/utils/browser.ts index 346dee58ee9..d47b8745e4b 100644 --- a/public/app/core/utils/browser.ts +++ b/public/app/core/utils/browser.ts @@ -32,3 +32,12 @@ export function checkBrowserCompatibility() { return true; } + +export function userAgentIsApple() { + const appleRe = /(iPhone|iPad|Mac)/; + return appleRe.test(navigator.platform); +} + +export function getModKey() { + return userAgentIsApple() ? 'cmd' : 'ctrl'; +}