mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Command palette topnav tweaks (#61991)
stylings tweaks to command palette
This commit is contained in:
@@ -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}>
|
||||
<div className={styles.searchContainer}>
|
||||
{isFetchingDashboardResults ? <Spinner className={styles.spinner} /> : <Icon name="search" size="md" />}
|
||||
<KBarSearch
|
||||
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search or jump to...')}
|
||||
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search Grafana')}
|
||||
className={styles.search}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.resultsContainer}>
|
||||
<RenderResults dashboardResults={dashboardResults} />
|
||||
</div>
|
||||
@@ -111,10 +114,14 @@ const RenderResults = ({ dashboardResults }: RenderResultsProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const getSearchStyles = (theme: GrafanaTheme2) => ({
|
||||
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',
|
||||
@@ -136,16 +143,24 @@ const getSearchStyles = (theme: GrafanaTheme2) => ({
|
||||
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({
|
||||
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}`,
|
||||
}),
|
||||
spinner: css({
|
||||
height: '22px',
|
||||
}),
|
||||
resultsContainer: css({
|
||||
paddingBottom: theme.spacing(1),
|
||||
@@ -162,4 +177,5 @@ const getSearchStyles = (theme: GrafanaTheme2) => ({
|
||||
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(() => {
|
||||
if (!searchQuery) {
|
||||
getRecentDashboardActions()
|
||||
.then((recentDashboardActions) => setStaticActions((v) => [...v, ...recentDashboardActions]))
|
||||
.then((recentDashboardActions) => setRecentDashboardActions(recentDashboardActions))
|
||||
.catch((err) => {
|
||||
console.error('Error loading recent dashboard actions', err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return staticActions;
|
||||
} else {
|
||||
setRecentDashboardActions([]);
|
||||
}
|
||||
}, [searchQuery]);
|
||||
|
||||
return [...recentDashboardActions, ...navTreeActions];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user