diff --git a/public/app/features/commandPalette/CommandPalette.tsx b/public/app/features/commandPalette/CommandPalette.tsx
index b70709e443e..7ee00813631 100644
--- a/public/app/features/commandPalette/CommandPalette.tsx
+++ b/public/app/features/commandPalette/CommandPalette.tsx
@@ -1,4 +1,4 @@
-import { css } from '@emotion/css';
+import { css, cx } from '@emotion/css';
import { useDialog } from '@react-aria/dialog';
import { FocusScope } from '@react-aria/focus';
import { useOverlay } from '@react-aria/overlays';
@@ -83,24 +83,33 @@ const RenderResults = ({ dashboardResults }: RenderResultsProps) => {
() => dashboardResults.map((dashboard) => new ActionImpl(dashboard, { store: {} })),
[dashboardResults]
);
+
const items = useMemo(
() => (dashboardResultItems.length > 0 ? [...results, dashboardsSectionTitle, ...dashboardResultItems] : results),
[results, dashboardsSectionTitle, dashboardResultItems]
);
return (
-
-
+ {
+ // These items are rendered in a container, in a virtual list, so we cannot
+ // use :first/last-child selectors, so we must mimic them in JS
+ const isFirstItem = items[0] === item;
+ const isLastItem = items[items.length - 1] === item;
+
+ const renderedItem =
typeof item === 'string' ? (
- {item}
+
) : (
- )
- }
- />
-
+ );
+
+ return isLastItem ? {renderedItem}
: renderedItem;
+ }}
+ />
);
};
@@ -137,15 +146,30 @@ const getSearchStyles = (theme: GrafanaTheme2) => ({
border: 'none',
background: theme.colors.background.canvas,
color: theme.colors.text.primary,
- borderBottom: `1px solid ${theme.colors.border.weak}`,
+ borderBottom: `1px solid ${theme.colors.border.medium}`,
}),
+
+ // Virtual list measures margin incorrectly, so we need to split padding before/after border
+ // over and inner and outer element
sectionHeader: css({
- padding: theme.spacing(1, 2),
+ paddingTop: theme.spacing(2),
fontSize: theme.typography.h6.fontSize,
fontWeight: theme.typography.body.fontWeight,
color: theme.colors.text.secondary,
}),
- resultsContainer: css({
- padding: theme.spacing(2, 0),
+ sectionHeaderInner: css({
+ padding: theme.spacing(1, 2),
+ borderTop: `1px solid ${theme.colors.border.medium}`,
+ }),
+
+ // We don't need the header above the first section
+ sectionHeaderInnerFirst: css({
+ borderTop: 'none',
+ paddingTop: 0,
+ }),
+
+ // Last item gets extra padding so it's not clipped by the rounded corners on the container
+ lastItem: css({
+ paddingBottom: theme.spacing(1),
}),
});
diff --git a/public/app/features/commandPalette/ResultItem.tsx b/public/app/features/commandPalette/ResultItem.tsx
index d82be2c2c89..fc4858fa00e 100644
--- a/public/app/features/commandPalette/ResultItem.tsx
+++ b/public/app/features/commandPalette/ResultItem.tsx
@@ -78,6 +78,7 @@ const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
alightItems: 'center',
justifyContent: 'space-between',
cursor: 'pointer',
+ position: 'relative',
'&:before': {
display: isActive ? 'block' : 'none',
content: '" "',
@@ -107,8 +108,8 @@ const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
fontSize: theme.typography.fontSize,
}),
breadcrumbAncestor: css({
- opacity: 0.5,
marginRight: theme.spacing(1),
+ color: theme.colors.text.secondary,
}),
subtitleText: css({
fontSize: theme.typography.fontSize - 2,