Navigation: Open command palette from search box (#61667)

* Reduce size of topnav search 'input' to 1/5th of the width, min width 200px

* Open command palette on topnav search box click

* Rename component

* fix comment

* feature flag the change

* update feature flag description
This commit is contained in:
Josh Hunt 2023-01-24 12:41:09 +00:00 committed by GitHub
parent ec171bcad5
commit 88347caf5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 4 deletions

View File

@ -46,6 +46,7 @@ Some stable features are enabled by default. You can disable a stable feature by
| `datasourceLogger` | Logs all datasource requests |
| `accessControlOnCall` | Access control primitives for OnCall |
| `alertingNoNormalState` | Stop maintaining state of alerts that are not firing |
| `topNavCommandPalette` | Launch the Command Palette from the top navigation search box |
## Alpha feature toggles

View File

@ -90,4 +90,5 @@ export interface FeatureToggles {
editPanelCSVDragAndDrop?: boolean;
alertingNoNormalState?: boolean;
azureMultipleResourcePicker?: boolean;
topNavCommandPalette?: boolean;
}

View File

@ -414,5 +414,11 @@ var (
Description: "Azure multiple resource picker",
State: FeatureStateAlpha,
},
{
Name: "topNavCommandPalette",
Description: "Launch the Command Palette from the top navigation search box",
State: FeatureStateBeta,
FrontendOnly: true,
},
}
)

View File

@ -302,4 +302,8 @@ const (
// FlagAzureMultipleResourcePicker
// Azure multiple resource picker
FlagAzureMultipleResourcePicker = "azureMultipleResourcePicker"
// FlagTopNavCommandPalette
// Launch the Command Palette from the top navigation search box
FlagTopNavCommandPalette = "topNavCommandPalette"
)

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Dropdown, ToolbarButton, useStyles2 } from '@grafana/ui';
import { config } from 'app/core/config';
import { contextSrv } from 'app/core/core';
import { useSelector } from 'app/types';
@ -14,6 +15,7 @@ import { QuickAdd } from './QuickAdd/QuickAdd';
import { SignInLink } from './TopBar/SignInLink';
import { TopNavBarMenu } from './TopBar/TopNavBarMenu';
import { TopSearchBarSection } from './TopBar/TopSearchBarSection';
import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger';
import { TopSearchBarInput } from './TopSearchBarInput';
import { TOP_BAR_LEVEL_HEIGHT } from './types';
@ -24,6 +26,13 @@ export function TopSearchBar() {
const helpNode = navIndex['help'];
const profileNode = navIndex['profile'];
const search =
config.featureToggles.commandPalette && config.featureToggles.topNavcommandPalette ? (
<TopSearchBarCommandPaletteTrigger />
) : (
<TopSearchBarInput />
);
return (
<div className={styles.layout}>
<TopSearchBarSection>
@ -32,9 +41,9 @@ export function TopSearchBar() {
</a>
<OrganizationSwitcher />
</TopSearchBarSection>
<TopSearchBarSection>
<TopSearchBarInput />
</TopSearchBarSection>
<TopSearchBarSection>{search}</TopSearchBarSection>
<TopSearchBarSection align="right">
<QuickAdd />
{helpNode && (
@ -70,7 +79,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
justifyContent: 'space-between',
[theme.breakpoints.up('sm')]: {
gridTemplateColumns: '1fr 1fr 1fr',
gridTemplateColumns: '2fr minmax(200px, 1fr) 2fr', // search should not be smaller than 200px
display: 'grid',
justifyContent: 'flex-start',

View File

@ -0,0 +1,109 @@
import { css } from '@emotion/css';
import { useKBar, VisualState } from 'kbar';
import React, { 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';
export function TopSearchBarCommandPaletteTrigger() {
const theme = useTheme2();
const { query: kbar } = useKBar((kbarState) => ({
kbarSearchQuery: kbarState.searchQuery,
kbarIsOpen: kbarState.visualState === VisualState.showing,
}));
const breakpoint = theme.breakpoints.values.sm;
const [isSmallScreen, setIsSmallScreen] = useState(window.matchMedia(`(max-width: ${breakpoint}px)`).matches);
useMediaQueryChange({
breakpoint,
onChange: (e) => {
setIsSmallScreen(e.matches);
},
});
const onOpenSearch = () => {
kbar.toggle();
};
if (isSmallScreen) {
return (
<ToolbarButton
iconOnly
icon="search"
aria-label={t('nav.search.placeholder', 'Search Grafana')}
onClick={onOpenSearch}
/>
);
}
return <PretendTextInput onClick={onOpenSearch} />;
}
function PretendTextInput({ onClick }: { onClick: () => void }) {
const styles = useStyles2(getStyles);
// 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
// action. You don't actually type into it.
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
<div className={styles.prefix}>
<Icon name="search" />
</div>
<button className={styles.fakeInput} onClick={onClick}>
{t('nav.search.placeholder', 'Search Grafana')}
</button>
</div>
</div>
);
}
const getStyles = (theme: GrafanaTheme2) => {
const baseStyles = getInputStyles({ theme });
return {
wrapper: baseStyles.wrapper,
inputWrapper: baseStyles.inputWrapper,
prefix: baseStyles.prefix,
fakeInput: css([
baseStyles.input,
{
textAlign: 'left',
paddingLeft: 28,
color: theme.colors.text.disabled,
// We want the focus styles to appear only when tabbing through, not when clicking the button
// (and when focus is restored after command palette closes)
'&:focus': {
outline: 'unset',
boxShadow: 'unset',
},
'&:focus-visible': css`
${focusCss(theme)}
`,
},
]),
button: css({
// height: 32,
width: '100%',
textAlign: 'center',
'> *': {
width: '100%',
textAlign: 'center',
justifyContent: 'center',
gap: '1ch',
},
}),
};
};