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
This commit is contained in:
@@ -10,7 +10,7 @@ import { PanelMenu } from './PanelMenu';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
menu: ReactElement | (() => ReactElement);
|
menu?: ReactElement | (() => ReactElement);
|
||||||
title?: string;
|
title?: string;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
dragClass?: string;
|
dragClass?: string;
|
||||||
@@ -41,17 +41,19 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
|
|||||||
style={{ top: `${offset}px` }}
|
style={{ top: `${offset}px` }}
|
||||||
data-testid="hover-header-container"
|
data-testid="hover-header-container"
|
||||||
>
|
>
|
||||||
<div
|
{dragClass && (
|
||||||
className={cx(styles.square, styles.draggable, dragClass)}
|
<div
|
||||||
onPointerDown={onPointerDown}
|
className={cx(styles.square, styles.draggable, dragClass)}
|
||||||
onPointerUp={onPointerUp}
|
onPointerDown={onPointerDown}
|
||||||
ref={draggableRef}
|
onPointerUp={onPointerUp}
|
||||||
>
|
ref={draggableRef}
|
||||||
<Icon name="expand-arrows" className={styles.draggableIcon} />
|
>
|
||||||
</div>
|
<Icon name="expand-arrows" className={styles.draggableIcon} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{!title && <h6 className={cx(styles.untitled, styles.draggable, dragClass)}>Untitled</h6>}
|
{!title && <h6 className={cx(styles.untitled, styles.draggable, dragClass)}>Untitled</h6>}
|
||||||
{children}
|
{children}
|
||||||
<div className={styles.square}>
|
{menu && (
|
||||||
<PanelMenu
|
<PanelMenu
|
||||||
menu={menu}
|
menu={menu}
|
||||||
title={title}
|
title={title}
|
||||||
@@ -59,7 +61,7 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
|
|||||||
menuButtonClass={styles.menuButton}
|
menuButtonClass={styles.menuButton}
|
||||||
onVisibleChange={setMenuOpen}
|
onVisibleChange={setMenuOpen}
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,6 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: theme.spacing(4),
|
width: theme.spacing(4),
|
||||||
height: '100%',
|
height: '100%',
|
||||||
paddingRight: theme.spacing(0.5),
|
|
||||||
}),
|
}),
|
||||||
draggable: css({
|
draggable: css({
|
||||||
cursor: 'move',
|
cursor: 'move',
|
||||||
@@ -109,12 +110,10 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
background: theme.colors.secondary.main,
|
background: theme.colors.secondary.main,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
title: css({
|
|
||||||
padding: theme.spacing(0.75),
|
|
||||||
}),
|
|
||||||
untitled: css({
|
untitled: css({
|
||||||
color: theme.colors.text.disabled,
|
color: theme.colors.text.disabled,
|
||||||
fontStyle: 'italic',
|
fontStyle: 'italic',
|
||||||
|
padding: theme.spacing(0, 1),
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
}),
|
}),
|
||||||
draggableIcon: css({
|
draggableIcon: css({
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -133,7 +133,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -170,7 +170,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -202,7 +202,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'white',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -232,7 +232,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -258,7 +258,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -285,7 +285,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -300,7 +300,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
### Extra options? Title Items
|
### Extra options? Title items and actions
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<PanelChrome
|
<PanelChrome
|
||||||
@@ -311,8 +311,12 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
<Button fill="text" icon="sliders-v-alt" variant="secondary" tooltip="extra content2 to render" />
|
<Button fill="text" icon="sliders-v-alt" variant="secondary" tooltip="extra content2 to render" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
description="Here I will put a description that explains a bit more this panel"
|
actions={
|
||||||
width={400}
|
<Button size="sm" variant="secondary" key="A">
|
||||||
|
Breakdown
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
width={500}
|
||||||
height={200}
|
height={200}
|
||||||
>
|
>
|
||||||
{(innerwidth, innerheight) => {
|
{(innerwidth, innerheight) => {
|
||||||
@@ -321,7 +325,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'white',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -337,14 +341,18 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
<Canvas>
|
<Canvas>
|
||||||
<PanelChrome
|
<PanelChrome
|
||||||
title="My awesome panel title"
|
title="My awesome panel title"
|
||||||
description="Here I will put a description that explains a bit more this panel"
|
|
||||||
titleItems={
|
titleItems={
|
||||||
<div>
|
<div>
|
||||||
<Button fill="text" icon="github" variant="secondary" tooltip="extra content to render" />
|
<Button fill="text" icon="github" variant="secondary" tooltip="extra content to render" />
|
||||||
<Button fill="text" icon="sliders-v-alt" variant="secondary" tooltip="extra content2 to render" />
|
<Button fill="text" icon="sliders-v-alt" variant="secondary" tooltip="extra content2 to render" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
width={400}
|
actions={
|
||||||
|
<Button size="sm" variant="secondary" key="A">
|
||||||
|
Breakdown
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
width={500}
|
||||||
height={200}
|
height={200}
|
||||||
>
|
>
|
||||||
{(innerwidth, innerheight) => {
|
{(innerwidth, innerheight) => {
|
||||||
@@ -353,7 +361,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -415,7 +423,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -470,7 +478,7 @@ Component used for rendering content wrapped in the same style as grafana panels
|
|||||||
style={{
|
style={{
|
||||||
width: innerwidth,
|
width: innerwidth,
|
||||||
height: innerheight,
|
height: innerheight,
|
||||||
background: 'gray',
|
background: 'rgba(230,0,0,0.05)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import React, { CSSProperties, useState, ReactNode } from 'react';
|
|||||||
import { useInterval } from 'react-use';
|
import { useInterval } from 'react-use';
|
||||||
|
|
||||||
import { LoadingState } from '@grafana/data';
|
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 { DashboardStoryCanvas } from '../../utils/storybook/DashboardStoryCanvas';
|
||||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
@@ -40,7 +40,7 @@ function getContentStyle(): CSSProperties {
|
|||||||
function renderPanel(name: string, overrides?: Partial<PanelChromeProps>) {
|
function renderPanel(name: string, overrides?: Partial<PanelChromeProps>) {
|
||||||
const props: PanelChromeProps = {
|
const props: PanelChromeProps = {
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 130,
|
height: 150,
|
||||||
children: () => undefined,
|
children: () => undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,10 +131,6 @@ export const Examples = () => {
|
|||||||
{renderPanel('No title, streaming loadingState', {
|
{renderPanel('No title, streaming loadingState', {
|
||||||
loadingState: LoadingState.Streaming,
|
loadingState: LoadingState.Streaming,
|
||||||
})}
|
})}
|
||||||
{renderPanel('No title, loading loadingState', {
|
|
||||||
loadingState: LoadingState.Loading,
|
|
||||||
})}
|
|
||||||
|
|
||||||
{renderPanel('Error status, menu', {
|
{renderPanel('Error status, menu', {
|
||||||
title: 'Default title',
|
title: 'Default title',
|
||||||
menu,
|
menu,
|
||||||
@@ -183,22 +179,120 @@ export const Examples = () => {
|
|||||||
/>,
|
/>,
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
{renderPanel('Deprecated error indicator, menu', {
|
|
||||||
title: 'Default title',
|
|
||||||
menu,
|
|
||||||
leftItems: [
|
|
||||||
<PanelChrome.ErrorIndicator
|
|
||||||
key="errorIndicator"
|
|
||||||
error="Error text"
|
|
||||||
onClick={action('ErrorIndicator: onClick fired')}
|
|
||||||
/>,
|
|
||||||
],
|
|
||||||
})}
|
|
||||||
{renderPanel('Display mode = transparent', {
|
{renderPanel('Display mode = transparent', {
|
||||||
title: 'Default title',
|
title: 'Default title',
|
||||||
displayMode: 'transparent',
|
displayMode: 'transparent',
|
||||||
menu,
|
menu,
|
||||||
leftItems: [],
|
})}
|
||||||
|
{renderPanel('Actions with button no menu', {
|
||||||
|
title: 'Actions with button no menu',
|
||||||
|
actions: (
|
||||||
|
<Button size="sm" variant="secondary" key="A">
|
||||||
|
Breakdown
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
{renderPanel('Panel with two actions', {
|
||||||
|
title: 'I have two buttons',
|
||||||
|
actions: [
|
||||||
|
<Button size="sm" variant="secondary" key="A">
|
||||||
|
Breakdown
|
||||||
|
</Button>,
|
||||||
|
<Button size="sm" variant="secondary" icon="times" key="B" />,
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
{renderPanel('With radio button', {
|
||||||
|
title: 'I have a radio button',
|
||||||
|
actions: [
|
||||||
|
<RadioButtonGroup
|
||||||
|
key="radio-button-group"
|
||||||
|
size="sm"
|
||||||
|
value="A"
|
||||||
|
options={[
|
||||||
|
{ label: 'Graph', value: 'A' },
|
||||||
|
{ label: 'Table', value: 'B' },
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
{renderPanel('Panel with action link', {
|
||||||
|
title: 'Panel with action link',
|
||||||
|
actions: (
|
||||||
|
<a className="external-link" href="/some/page">
|
||||||
|
Error details
|
||||||
|
<Icon name="arrow-right" />
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
{renderPanel('Action and menu (should be rare)', {
|
||||||
|
title: 'Action and menu',
|
||||||
|
menu,
|
||||||
|
actions: (
|
||||||
|
<Button size="sm" variant="secondary">
|
||||||
|
Breakdown
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</HorizontalGroup>
|
||||||
|
</div>
|
||||||
|
</DashboardStoryCanvas>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ExamplesHoverHeader = () => {
|
||||||
|
return (
|
||||||
|
<DashboardStoryCanvas>
|
||||||
|
<div>
|
||||||
|
<HorizontalGroup spacing="md" align="flex-start" wrap>
|
||||||
|
{renderPanel('Title items, menu, hover header', {
|
||||||
|
title: 'Default title',
|
||||||
|
description: 'This is a description',
|
||||||
|
menu,
|
||||||
|
hoverHeader: true,
|
||||||
|
dragClass: 'draggable',
|
||||||
|
titleItems: (
|
||||||
|
<PanelChrome.TitleItem title="Online">
|
||||||
|
<Icon name="heart" />
|
||||||
|
</PanelChrome.TitleItem>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
{renderPanel('Multiple title items', {
|
||||||
|
title: 'Default title',
|
||||||
|
menu,
|
||||||
|
hoverHeader: true,
|
||||||
|
dragClass: 'draggable',
|
||||||
|
titleItems: [
|
||||||
|
<PanelChrome.TitleItem title="Online" key="A">
|
||||||
|
<Icon name="heart" />
|
||||||
|
</PanelChrome.TitleItem>,
|
||||||
|
<PanelChrome.TitleItem title="Link" key="B" onClick={() => {}}>
|
||||||
|
<Icon name="external-link-alt" />
|
||||||
|
</PanelChrome.TitleItem>,
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
{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: (
|
||||||
|
<a className="external-link" href="/some/page">
|
||||||
|
Error details
|
||||||
|
<Icon name="arrow-right" />
|
||||||
|
</a>
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,13 +47,10 @@ export interface PanelChromeProps {
|
|||||||
*/
|
*/
|
||||||
statusMessageOnClick?: (e: React.SyntheticEvent) => void;
|
statusMessageOnClick?: (e: React.SyntheticEvent) => void;
|
||||||
/**
|
/**
|
||||||
* @deprecated in favor of props
|
* @deprecated use `actions' instead
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
leftItems?: ReactNode[];
|
leftItems?: ReactNode[];
|
||||||
|
actions?: ReactNode;
|
||||||
displayMode?: 'default' | 'transparent';
|
displayMode?: 'default' | 'transparent';
|
||||||
onCancelQuery?: () => void;
|
onCancelQuery?: () => void;
|
||||||
}
|
}
|
||||||
@@ -84,6 +81,7 @@ export function PanelChrome({
|
|||||||
statusMessage,
|
statusMessage,
|
||||||
statusMessageOnClick,
|
statusMessageOnClick,
|
||||||
leftItems,
|
leftItems,
|
||||||
|
actions,
|
||||||
onCancelQuery,
|
onCancelQuery,
|
||||||
}: PanelChromeProps) {
|
}: PanelChromeProps) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
@@ -111,6 +109,11 @@ export function PanelChrome({
|
|||||||
containerStyles.border = 'none';
|
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 ariaLabel = title ? selectors.components.Panels.Panel.containerByTitle(title) : 'Panel';
|
||||||
|
|
||||||
const headerContent = (
|
const headerContent = (
|
||||||
@@ -142,6 +145,9 @@ export function PanelChrome({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</DelayRender>
|
</DelayRender>
|
||||||
)}
|
)}
|
||||||
|
<div className={styles.rightAligned}>
|
||||||
|
{actions && <div className={styles.rightActions}>{itemsRenderer(actions, (item) => item)}</div>}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -153,11 +159,10 @@ export function PanelChrome({
|
|||||||
|
|
||||||
{hoverHeader && !isTouchDevice && (
|
{hoverHeader && !isTouchDevice && (
|
||||||
<>
|
<>
|
||||||
{menu && (
|
<HoverWidget menu={menu} title={title} offset={hoverHeaderOffset} dragClass={dragClass}>
|
||||||
<HoverWidget menu={menu} title={title} offset={hoverHeaderOffset} dragClass={dragClass}>
|
{headerContent}
|
||||||
{headerContent}
|
</HoverWidget>
|
||||||
</HoverWidget>
|
|
||||||
)}
|
|
||||||
{statusMessage && (
|
{statusMessage && (
|
||||||
<div className={styles.errorContainerFloating}>
|
<div className={styles.errorContainerFloating}>
|
||||||
<PanelStatus message={statusMessage} onClick={statusMessageOnClick} ariaLabel="Panel status" />
|
<PanelStatus message={statusMessage} onClick={statusMessageOnClick} ariaLabel="Panel status" />
|
||||||
@@ -176,23 +181,19 @@ export function PanelChrome({
|
|||||||
|
|
||||||
{headerContent}
|
{headerContent}
|
||||||
|
|
||||||
<div className={styles.rightAligned}>
|
{menu && (
|
||||||
{menu && (
|
<PanelMenu
|
||||||
<PanelMenu
|
menu={menu}
|
||||||
menu={menu}
|
title={title}
|
||||||
title={title}
|
placement="bottom-end"
|
||||||
placement="bottom-end"
|
menuButtonClass={cx(
|
||||||
menuButtonClass={cx(
|
{ [styles.hiddenMenu]: !isTouchDevice },
|
||||||
{ [styles.hiddenMenu]: !isTouchDevice },
|
styles.menuItem,
|
||||||
styles.menuItem,
|
dragClassCancel,
|
||||||
dragClassCancel,
|
showOnHoverClass
|
||||||
showOnHoverClass
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
{leftItems && <div className={styles.leftItems}>{itemsRenderer(leftItems, (item) => item)}</div>}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -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);
|
const toRender = React.Children.toArray(items).filter(Boolean);
|
||||||
return toRender.length > 0 ? renderer(toRender) : null;
|
return toRender.length > 0 ? renderer(toRender) : null;
|
||||||
};
|
};
|
||||||
@@ -339,9 +340,10 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
top: 0,
|
top: 0,
|
||||||
zIndex: theme.zIndex.tooltip,
|
zIndex: theme.zIndex.tooltip,
|
||||||
}),
|
}),
|
||||||
leftItems: css({
|
rightActions: css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
paddingRight: theme.spacing(padding),
|
padding: theme.spacing(0, padding),
|
||||||
|
gap: theme.spacing(1),
|
||||||
}),
|
}),
|
||||||
rightAligned: css({
|
rightAligned: css({
|
||||||
label: 'right-aligned-container',
|
label: 'right-aligned-container',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function PanelDescription({ description, className }: Props) {
|
|||||||
return description !== '' ? (
|
return description !== '' ? (
|
||||||
<Tooltip interactive content={getDescriptionContent}>
|
<Tooltip interactive content={getDescriptionContent}>
|
||||||
<TitleItem className={cx(className, styles.description)}>
|
<TitleItem className={cx(className, styles.description)}>
|
||||||
<Icon name="info-circle" size="md" title="description" />
|
<Icon name="info-circle" size="md" />
|
||||||
</TitleItem>
|
</TitleItem>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : null;
|
) : null;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const DashboardStoryCanvas = ({ children }: Props) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
background: ${theme.colors.background.canvas};
|
background: ${theme.colors.background.canvas};
|
||||||
|
overflow: auto;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return <div className={style}>{children}</div>;
|
return <div className={style}>{children}</div>;
|
||||||
|
|||||||
Reference in New Issue
Block a user