Navigation: Command palette topnav tweaks (#61991)

stylings tweaks to command palette
pull/62182/head
Ashley Harrison 2 years ago committed by GitHub
parent c4e067d49d
commit a3b396854a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      public/app/core/components/AppChrome/TopSearchBar.tsx
  2. 136
      public/app/features/commandPalette/CommandPalette.tsx
  3. 14
      public/app/features/commandPalette/actions/dashboardActions.ts
  4. 21
      public/app/features/commandPalette/actions/staticActions.ts
  5. 28
      public/app/features/commandPalette/actions/useActions.ts

@ -79,7 +79,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
justifyContent: 'space-between',
[theme.breakpoints.up('sm')]: {
gridTemplateColumns: '2fr minmax(200px, 1fr) 2fr', // search should not be smaller than 200px
gridTemplateColumns: '1.5fr minmax(200px, 1fr) 1.5fr', // search should not be smaller than 200px
display: 'grid',
justifyContent: 'flex-start',

@ -16,8 +16,8 @@ import {
import React, { useEffect, useMemo, useRef } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui';
import { config, reportInteraction } from '@grafana/runtime';
import { Icon, Spinner, useStyles2 } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { ResultItem } from './ResultItem';
@ -34,9 +34,9 @@ export const CommandPalette = () => {
searchQuery: state.searchQuery,
}));
const actions = useActions();
const actions = useActions(searchQuery);
useRegisterActions(actions, [actions]);
const dashboardResults = useDashboardResults(searchQuery, showing);
const { dashboardResults, isFetchingDashboardResults } = useDashboardResults(searchQuery, showing);
const ref = useRef<HTMLDivElement>(null);
const { overlayProps } = useOverlay(
@ -56,10 +56,13 @@ export const CommandPalette = () => {
<KBarAnimator className={styles.animator}>
<FocusScope contain autoFocus restoreFocus>
<div {...overlayProps} {...dialogProps}>
<KBarSearch
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search or jump to...')}
className={styles.search}
/>
<div className={styles.searchContainer}>
{isFetchingDashboardResults ? <Spinner className={styles.spinner} /> : <Icon name="search" size="md" />}
<KBarSearch
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search Grafana')}
className={styles.search}
/>
</div>
<div className={styles.resultsContainer}>
<RenderResults dashboardResults={dashboardResults} />
</div>
@ -111,55 +114,68 @@ const RenderResults = ({ dashboardResults }: RenderResultsProps) => {
);
};
const getSearchStyles = (theme: GrafanaTheme2) => ({
positioner: css({
zIndex: theme.zIndex.portal,
marginTop: '0px',
'&::before': {
content: '""',
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
background: theme.components.overlay.background,
backdropFilter: 'blur(1px)',
},
}),
animator: css({
maxWidth: theme.breakpoints.values.md,
width: '100%',
background: theme.colors.background.primary,
color: theme.colors.text.primary,
borderRadius: theme.shape.borderRadius(2),
border: `1px solid ${theme.colors.border.weak}`,
overflow: 'hidden',
boxShadow: theme.shadows.z3,
}),
search: css({
padding: theme.spacing(1.5, 2),
fontSize: theme.typography.fontSize,
width: '100%',
boxSizing: 'border-box',
outline: 'none',
border: 'none',
background: theme.components.input.background,
color: theme.components.input.text,
borderBottom: `1px solid ${theme.colors.border.weak}`,
}),
resultsContainer: css({
paddingBottom: theme.spacing(1),
}),
sectionHeader: css({
padding: theme.spacing(1.5, 2, 1, 2),
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.primary,
borderTop: `1px solid ${theme.colors.border.weak}`,
marginTop: theme.spacing(1),
}),
sectionHeaderFirst: css({
borderTop: 'none',
marginTop: 0,
}),
});
const getSearchStyles = (theme: GrafanaTheme2) => {
const topNavCommandPalette = Boolean(config.featureToggles.topNavCommandPalette);
return {
positioner: css({
zIndex: theme.zIndex.portal,
marginTop: '0px',
paddingTop: topNavCommandPalette ? '4px !important' : undefined,
'&::before': {
content: '""',
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
background: theme.components.overlay.background,
backdropFilter: 'blur(1px)',
},
}),
animator: css({
maxWidth: theme.breakpoints.values.md,
width: '100%',
background: theme.colors.background.primary,
color: theme.colors.text.primary,
borderRadius: theme.shape.borderRadius(2),
border: `1px solid ${theme.colors.border.weak}`,
overflow: 'hidden',
boxShadow: theme.shadows.z3,
}),
searchContainer: css({
alignItems: 'center',
background: theme.components.input.background,
borderBottom: `1px solid ${theme.colors.border.weak}`,
display: 'flex',
gap: theme.spacing(1),
padding: theme.spacing(1, 2),
}),
search: css({
fontSize: theme.typography.fontSize,
width: '100%',
boxSizing: 'border-box',
outline: 'none',
border: 'none',
color: theme.components.input.text,
}),
spinner: css({
height: '22px',
}),
resultsContainer: css({
paddingBottom: theme.spacing(1),
}),
sectionHeader: css({
padding: theme.spacing(1.5, 2, 1, 2),
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.primary,
borderTop: `1px solid ${theme.colors.border.weak}`,
marginTop: theme.spacing(1),
}),
sectionHeaderFirst: css({
borderTop: 'none',
marginTop: 0,
}),
};
};

@ -34,7 +34,7 @@ export async function getRecentDashboardActions(): Promise<CommandPaletteAction[
const recentDashboardActions: CommandPaletteAction[] = recentResults.map((item) => {
const { url, name } = item; // items are backed by DataFrameView, so must hold the url in a closure
return {
id: `recent-dashboards/${url}`,
id: `recent-dashboards${url}`,
name: `${name}`,
section: t('command-palette.section.recent-dashboards', 'Recent dashboards'),
priority: RECENT_DASHBOARDS_PRORITY,
@ -62,7 +62,7 @@ export async function getDashboardSearchResultActions(searchQuery: string): Prom
const goToDashboardActions: CommandPaletteAction[] = data.view.map((item) => {
const { url, name } = item; // items are backed by DataFrameView, so must hold the url in a closure
return {
id: `go/dashboard/${url}`,
id: `go/dashboard${url}`,
name: `${name}`,
section: t('command-palette.section.dashboard-search-results', 'Dashboards'),
priority: SEARCH_RESULTS_PRORITY,
@ -77,15 +77,23 @@ export async function getDashboardSearchResultActions(searchQuery: string): Prom
export function useDashboardResults(searchQuery: string, isShowing: boolean) {
const [dashboardResults, setDashboardResults] = useState<CommandPaletteAction[]>([]);
const [isFetchingDashboardResults, setIsFetchingDashboardResults] = useState(false);
// Hit dashboards API
useEffect(() => {
if (isShowing && searchQuery.length > 0) {
setIsFetchingDashboardResults(true);
debouncedDashboardSearch(searchQuery).then((resultActions) => {
setDashboardResults(resultActions);
setIsFetchingDashboardResults(false);
});
} else {
setDashboardResults([]);
}
}, [isShowing, searchQuery]);
return dashboardResults;
return {
dashboardResults,
isFetchingDashboardResults,
};
}

@ -1,5 +1,5 @@
import { locationUtil, NavModelItem } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { config, locationService } from '@grafana/runtime';
import { t } from 'app/core/internationalization';
import { changeTheme } from 'app/core/services/theme';
@ -49,14 +49,6 @@ function navTreeToActions(navTree: NavModelItem[], parent?: NavModelItem): Comma
export default (navBarTree: NavModelItem[]): CommandPaletteAction[] => {
const globalActions: CommandPaletteAction[] = [
{
id: 'go/search',
name: t('command-palette.action.search', 'Search'),
keywords: 'navigate',
perform: () => locationService.push('?search=open'),
section: t('command-palette.section.pages', 'Pages'),
priority: DEFAULT_PRIORITY,
},
{
id: 'preferences/theme',
name: t('command-palette.action.change-theme', 'Change theme...'),
@ -82,6 +74,17 @@ export default (navBarTree: NavModelItem[]): CommandPaletteAction[] => {
},
];
if (!config.featureToggles.topNavCommandPalette) {
globalActions.unshift({
id: 'go/search',
name: t('command-palette.action.search', 'Search'),
keywords: 'navigate',
perform: () => locationService.push('?search=open'),
section: t('command-palette.section.pages', 'Pages'),
priority: DEFAULT_PRIORITY,
});
}
const navBarActions = navTreeToActions(navBarTree);
return [...globalActions, ...navBarActions];

@ -7,29 +7,33 @@ import { CommandPaletteAction } from '../types';
import { getRecentDashboardActions } from './dashboardActions';
import getStaticActions from './staticActions';
export default function useActions() {
const [staticActions, setStaticActions] = useState<CommandPaletteAction[]>([]);
export default function useActions(searchQuery: string) {
const [navTreeActions, setNavTreeActions] = useState<CommandPaletteAction[]>([]);
const [recentDashboardActions, setRecentDashboardActions] = useState<CommandPaletteAction[]>([]);
const { navBarTree } = useSelector((state) => {
return {
navBarTree: state.navBarTree,
};
});
// Load standard static actions
useEffect(() => {
const staticActionsResp = getStaticActions(navBarTree);
setStaticActions(staticActionsResp);
setNavTreeActions(staticActionsResp);
}, [navBarTree]);
// Load recent dashboards - we don't want them to reload when the nav tree changes
useEffect(() => {
getRecentDashboardActions()
.then((recentDashboardActions) => setStaticActions((v) => [...v, ...recentDashboardActions]))
.catch((err) => {
console.error('Error loading recent dashboard actions', err);
});
}, []);
return staticActions;
if (!searchQuery) {
getRecentDashboardActions()
.then((recentDashboardActions) => setRecentDashboardActions(recentDashboardActions))
.catch((err) => {
console.error('Error loading recent dashboard actions', err);
});
} else {
setRecentDashboardActions([]);
}
}, [searchQuery]);
return [...recentDashboardActions, ...navTreeActions];
}

Loading…
Cancel
Save