From a3b396854a4ee37e98d7a7ddfd4d1e3be5c551b3 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Thu, 26 Jan 2023 10:03:16 +0000 Subject: [PATCH] Navigation: Command palette topnav tweaks (#61991) stylings tweaks to command palette --- .../components/AppChrome/TopSearchBar.tsx | 2 +- .../commandPalette/CommandPalette.tsx | 136 ++++++++++-------- .../actions/dashboardActions.ts | 14 +- .../commandPalette/actions/staticActions.ts | 21 +-- .../commandPalette/actions/useActions.ts | 26 ++-- 5 files changed, 115 insertions(+), 84 deletions(-) diff --git a/public/app/core/components/AppChrome/TopSearchBar.tsx b/public/app/core/components/AppChrome/TopSearchBar.tsx index bde15eea252..b6246da47bd 100644 --- a/public/app/core/components/AppChrome/TopSearchBar.tsx +++ b/public/app/core/components/AppChrome/TopSearchBar.tsx @@ -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', diff --git a/public/app/features/commandPalette/CommandPalette.tsx b/public/app/features/commandPalette/CommandPalette.tsx index 3c3af74f770..73fb7bf5778 100644 --- a/public/app/features/commandPalette/CommandPalette.tsx +++ b/public/app/features/commandPalette/CommandPalette.tsx @@ -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(null); const { overlayProps } = useOverlay( @@ -56,10 +56,13 @@ export const CommandPalette = () => {
- +
+ {isFetchingDashboardResults ? : } + +
@@ -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, + }), + }; +}; diff --git a/public/app/features/commandPalette/actions/dashboardActions.ts b/public/app/features/commandPalette/actions/dashboardActions.ts index 8c8647293cd..f9f4c24ba69 100644 --- a/public/app/features/commandPalette/actions/dashboardActions.ts +++ b/public/app/features/commandPalette/actions/dashboardActions.ts @@ -34,7 +34,7 @@ export async function getRecentDashboardActions(): Promise { 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([]); + 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, + }; } diff --git a/public/app/features/commandPalette/actions/staticActions.ts b/public/app/features/commandPalette/actions/staticActions.ts index 835cec7be4f..893adf713e5 100644 --- a/public/app/features/commandPalette/actions/staticActions.ts +++ b/public/app/features/commandPalette/actions/staticActions.ts @@ -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]; diff --git a/public/app/features/commandPalette/actions/useActions.ts b/public/app/features/commandPalette/actions/useActions.ts index 7dc92ff601d..15f5547266f 100644 --- a/public/app/features/commandPalette/actions/useActions.ts +++ b/public/app/features/commandPalette/actions/useActions.ts @@ -7,29 +7,33 @@ import { CommandPaletteAction } from '../types'; import { getRecentDashboardActions } from './dashboardActions'; import getStaticActions from './staticActions'; -export default function useActions() { - const [staticActions, setStaticActions] = useState([]); +export default function useActions(searchQuery: string) { + const [navTreeActions, setNavTreeActions] = useState([]); + const [recentDashboardActions, setRecentDashboardActions] = useState([]); 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); - }); - }, []); + if (!searchQuery) { + getRecentDashboardActions() + .then((recentDashboardActions) => setRecentDashboardActions(recentDashboardActions)) + .catch((err) => { + console.error('Error loading recent dashboard actions', err); + }); + } else { + setRecentDashboardActions([]); + } + }, [searchQuery]); - return staticActions; + return [...recentDashboardActions, ...navTreeActions]; }