diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx
index 584bc082c4b..42a879a57ea 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx
@@ -236,6 +236,11 @@ export const Examples = () => {
title: 'Default title',
collapsible: true,
})}
+ {renderPanel('Menu always visible', {
+ title: 'Menu always visible',
+ showMenuAlways: true,
+ menu,
+ })}
{renderPanel('Panel with action link', {
title: 'Panel with action link',
actions: (
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
index d9acab30c2d..bb3d63d723f 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
@@ -111,6 +111,13 @@ it('renders panel with a show-on-hover menu icon if prop menu', () => {
expect(screen.getByTestId('panel-menu-button')).not.toBeVisible();
});
+it('renders panel with an always visible menu icon if prop showMenuAlways is true', () => {
+ setup({ menu:
Menu
, showMenuAlways: true });
+
+ expect(screen.getByTestId('panel-menu-button')).toBeInTheDocument();
+ expect(screen.getByTestId('panel-menu-button')).toBeVisible();
+});
+
it('renders error status in the panel header if any given', () => {
setup({ statusMessage: 'Error test' });
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
index e50f103339f..14da2a42c06 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
@@ -83,6 +83,10 @@ interface AutoSize extends BaseProps {
interface Collapsible {
collapsible: boolean;
collapsed?: boolean;
+ /**
+ * If true, the VizPanelMenu will always be visible in the panel header. Defaults to false.
+ */
+ showMenuAlways?: boolean;
/**
* callback when collapsing or expanding the panel
*/
@@ -94,6 +98,7 @@ interface Collapsible {
interface HoverHeader {
collapsible?: never;
collapsed?: never;
+ showMenuAlways?: never;
onToggleCollapse?: never;
hoverHeader?: boolean;
hoverHeaderOffset?: number;
@@ -134,6 +139,7 @@ export function PanelChrome({
onFocus,
onMouseMove,
onMouseEnter,
+ showMenuAlways = false,
}: PanelChromeProps) {
const theme = useTheme2();
const styles = useStyles2(getStyles);
@@ -150,7 +156,7 @@ export function PanelChrome({
}
// hover menu is only shown on hover when not on touch devices
- const showOnHoverClass = 'show-on-hover';
+ const showOnHoverClass = showMenuAlways ? 'always-show' : 'show-on-hover';
const isPanelTransparent = displayMode === 'transparent';
const headerHeight = getHeaderHeight(theme, hasHeader);
@@ -390,6 +396,13 @@ const getStyles = (theme: GrafanaTheme2) => {
display: 'flex',
flexDirection: 'column',
+ '.always-show': {
+ background: 'none',
+ '&:focus-visible, &:hover': {
+ background: theme.colors.secondary.shade,
+ },
+ },
+
'.show-on-hover': {
opacity: '0',
visibility: 'hidden',