From c63cb5a0bdd415a67c68352f08922f03d0b033f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 3 Apr 2023 12:44:00 +0200 Subject: [PATCH] PanelChrome: Add option to show actions on the right side (actions = leftItems) (#65762) * PanelChrome: Add option to show actions on the right side * remove button style change * Added docs and minor tweaks to align the type with titleItems * Hover header fixes, storybook improvements, and title description fix * Fixed condition for drag icon in hover header --- .../components/PanelChrome/HoverWidget.tsx | 29 ++-- .../components/PanelChrome/PanelChrome.mdx | 40 +++--- .../PanelChrome/PanelChrome.story.tsx | 130 +++++++++++++++--- .../components/PanelChrome/PanelChrome.tsx | 64 ++++----- .../PanelChrome/PanelDescription.tsx | 2 +- .../utils/storybook/DashboardStoryCanvas.tsx | 1 + 6 files changed, 185 insertions(+), 81 deletions(-) diff --git a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx index 188f6c8158d..6a78d7d9389 100644 --- a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx @@ -10,7 +10,7 @@ import { PanelMenu } from './PanelMenu'; interface Props { children?: React.ReactNode; - menu: ReactElement | (() => ReactElement); + menu?: ReactElement | (() => ReactElement); title?: string; offset?: number; dragClass?: string; @@ -41,17 +41,19 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }: style={{ top: `${offset}px` }} data-testid="hover-header-container" > -
- -
+ {dragClass && ( +
+ +
+ )} {!title &&
Untitled
} {children} -
+ {menu && ( -
+ )} ); } @@ -92,7 +94,6 @@ function getStyles(theme: GrafanaTheme2) { alignItems: 'center', width: theme.spacing(4), height: '100%', - paddingRight: theme.spacing(0.5), }), draggable: css({ cursor: 'move', @@ -109,12 +110,10 @@ function getStyles(theme: GrafanaTheme2) { background: theme.colors.secondary.main, }, }), - title: css({ - padding: theme.spacing(0.75), - }), untitled: css({ color: theme.colors.text.disabled, fontStyle: 'italic', + padding: theme.spacing(0, 1), marginBottom: 0, }), draggableIcon: css({ diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.mdx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.mdx index 868b714d1b3..d635f6c6371 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.mdx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.mdx @@ -56,7 +56,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -133,7 +133,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -170,7 +170,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -202,7 +202,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'white', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -232,7 +232,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -258,7 +258,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -285,7 +285,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -300,7 +300,7 @@ Component used for rendering content wrapped in the same style as grafana panels -### Extra options? Title Items +### Extra options? Title items and actions ```tsx } - description="Here I will put a description that explains a bit more this panel" - width={400} + actions={ + + } + width={500} height={200} > {(innerwidth, innerheight) => { @@ -321,7 +325,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'white', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -337,14 +341,18 @@ Component used for rendering content wrapped in the same style as grafana panels + } + width={500} height={200} > {(innerwidth, innerheight) => { @@ -353,7 +361,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -415,7 +423,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -470,7 +478,7 @@ Component used for rendering content wrapped in the same style as grafana panels style={{ width: innerwidth, height: innerheight, - background: 'gray', + background: 'rgba(230,0,0,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx index 37ac6d5b1c5..3267a653a23 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx @@ -5,7 +5,7 @@ import React, { CSSProperties, useState, ReactNode } from 'react'; import { useInterval } from 'react-use'; import { LoadingState } from '@grafana/data'; -import { PanelChrome, PanelChromeProps } from '@grafana/ui'; +import { Button, Icon, PanelChrome, PanelChromeProps, RadioButtonGroup } from '@grafana/ui'; import { DashboardStoryCanvas } from '../../utils/storybook/DashboardStoryCanvas'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; @@ -40,7 +40,7 @@ function getContentStyle(): CSSProperties { function renderPanel(name: string, overrides?: Partial) { const props: PanelChromeProps = { width: 400, - height: 130, + height: 150, children: () => undefined, }; @@ -131,10 +131,6 @@ export const Examples = () => { {renderPanel('No title, streaming loadingState', { loadingState: LoadingState.Streaming, })} - {renderPanel('No title, loading loadingState', { - loadingState: LoadingState.Loading, - })} - {renderPanel('Error status, menu', { title: 'Default title', menu, @@ -183,22 +179,120 @@ export const Examples = () => { />, ], })} - {renderPanel('Deprecated error indicator, menu', { - title: 'Default title', - menu, - leftItems: [ - , - ], - })} {renderPanel('Display mode = transparent', { title: 'Default title', displayMode: 'transparent', menu, - leftItems: [], + })} + {renderPanel('Actions with button no menu', { + title: 'Actions with button no menu', + actions: ( + + ), + })} + {renderPanel('Panel with two actions', { + title: 'I have two buttons', + actions: [ + , + + ), + })} + + + + ); +}; + +export const ExamplesHoverHeader = () => { + return ( + +
+ + {renderPanel('Title items, menu, hover header', { + title: 'Default title', + description: 'This is a description', + menu, + hoverHeader: true, + dragClass: 'draggable', + titleItems: ( + + + + ), + })} + {renderPanel('Multiple title items', { + title: 'Default title', + menu, + hoverHeader: true, + dragClass: 'draggable', + titleItems: [ + + + , + {}}> + + , + ], + })} + {renderPanel('Hover header, loading loadingState', { + loadingState: LoadingState.Loading, + hoverHeader: true, + title: 'I am a hover header', + dragClass: 'draggable', + })} + {renderPanel('No title, Hover header', { + hoverHeader: true, + dragClass: 'draggable', + })} + {renderPanel('Should not have drag icon', { + title: 'No drag icon', + hoverHeader: true, + })} + {renderPanel('With action link', { + title: 'With link in hover header', + hoverHeader: true, + actions: ( + + Error details + + + ), })}
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx index 626326f3cf9..b98b816908e 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx @@ -47,13 +47,10 @@ export interface PanelChromeProps { */ statusMessageOnClick?: (e: React.SyntheticEvent) => void; /** - * @deprecated in favor of props - * statusMessage for error messages - * and loadingState for loading and streaming data - * which will serve the same purpose - * of showing/interacting with the panel's state - */ + * @deprecated use `actions' instead + **/ leftItems?: ReactNode[]; + actions?: ReactNode; displayMode?: 'default' | 'transparent'; onCancelQuery?: () => void; } @@ -84,6 +81,7 @@ export function PanelChrome({ statusMessage, statusMessageOnClick, leftItems, + actions, onCancelQuery, }: PanelChromeProps) { const theme = useTheme2(); @@ -111,6 +109,11 @@ export function PanelChrome({ containerStyles.border = 'none'; } + /** Old property name now maps to actions */ + if (leftItems) { + actions = leftItems; + } + const ariaLabel = title ? selectors.components.Panels.Panel.containerByTitle(title) : 'Panel'; const headerContent = ( @@ -142,6 +145,9 @@ export function PanelChrome({ )} +
+ {actions &&
{itemsRenderer(actions, (item) => item)}
} +
); @@ -153,11 +159,10 @@ export function PanelChrome({ {hoverHeader && !isTouchDevice && ( <> - {menu && ( - - {headerContent} - - )} + + {headerContent} + + {statusMessage && (
@@ -176,23 +181,19 @@ export function PanelChrome({ {headerContent} -
- {menu && ( - - )} - - {leftItems &&
{itemsRenderer(leftItems, (item) => item)}
} -
+ {menu && ( + + )}
)} @@ -203,7 +204,7 @@ export function PanelChrome({ ); } -const itemsRenderer = (items: ReactNode[], renderer: (items: ReactNode[]) => ReactNode): ReactNode => { +const itemsRenderer = (items: ReactNode[] | ReactNode, renderer: (items: ReactNode[]) => ReactNode): ReactNode => { const toRender = React.Children.toArray(items).filter(Boolean); return toRender.length > 0 ? renderer(toRender) : null; }; @@ -339,9 +340,10 @@ const getStyles = (theme: GrafanaTheme2) => { top: 0, zIndex: theme.zIndex.tooltip, }), - leftItems: css({ + rightActions: css({ display: 'flex', - paddingRight: theme.spacing(padding), + padding: theme.spacing(0, padding), + gap: theme.spacing(1), }), rightAligned: css({ label: 'right-aligned-container', diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx index d5030e70c4e..0f7909cada4 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx @@ -31,7 +31,7 @@ export function PanelDescription({ description, className }: Props) { return description !== '' ? ( - + ) : null; diff --git a/packages/grafana-ui/src/utils/storybook/DashboardStoryCanvas.tsx b/packages/grafana-ui/src/utils/storybook/DashboardStoryCanvas.tsx index dbf458df7a8..c69adfe2d45 100644 --- a/packages/grafana-ui/src/utils/storybook/DashboardStoryCanvas.tsx +++ b/packages/grafana-ui/src/utils/storybook/DashboardStoryCanvas.tsx @@ -14,6 +14,7 @@ export const DashboardStoryCanvas = ({ children }: Props) => { height: 100%; padding: 32px; background: ${theme.colors.background.canvas}; + overflow: auto; `; return
{children}
;