PanelChrome: Simplify props and code a bit (#61595)

* PanelChrome: Simplify props and code a bit

* Don't set a default empty array for leftItems so we can know user has set it

* fix 'this' is undefined

* add storybook examples of statusMessage; remove SB examples of loadingState === Error; simplify loadingState checks in PanelChrome;

Co-authored-by: polinaboneva <polina.boneva@grafana.com>
This commit is contained in:
Torkel Ödegaard 2023-01-17 09:52:14 +01:00 committed by GitHub
parent 54fd1b634f
commit c1d3b59643
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 62 deletions

View File

@ -93,34 +93,33 @@ export const Examples = () => {
<DashboardStoryCanvas>
<HorizontalGroup spacing="md" align="flex-start">
<VerticalGroup spacing="md">
{renderPanel('Error status', {
{renderPanel('Has statusMessage', {
title: 'Default title',
status: {
message: 'Error text',
onClick: action('ErrorIndicator: onClick fired'),
},
statusMessage: 'Error text',
statusMessageOnClick: action('ErrorIndicator: onClick fired'),
})}
{renderPanel('No padding, error loadingState', {
{renderPanel('No padding, has statusMessage', {
padding: 'none',
title: 'Default title',
statusMessage: 'Error text',
statusMessageOnClick: action('ErrorIndicator: onClick fired'),
})}
{renderPanel('No title, loadingState is Error, no statusMessage', {
loadingState: LoadingState.Error,
})}
{renderPanel('No title, error loadingState', {
loadingState: LoadingState.Error,
})}
{renderPanel('Streaming loadingState', {
{renderPanel('loadingState is Streaming', {
title: 'Default title',
loadingState: LoadingState.Streaming,
})}
{renderPanel('Loading loadingState', {
{renderPanel('loadingState is Loading', {
title: 'Default title',
loadingState: LoadingState.Loading,
})}
</VerticalGroup>
<VerticalGroup spacing="md">
{renderPanel('Default panel: no non-required props')}
{renderPanel('No padding, no title', {
{renderPanel('No padding', {
padding: 'none',
})}
{renderPanel('Very long title', {
@ -137,28 +136,27 @@ export const Examples = () => {
{renderPanel('Error status, menu', {
title: 'Default title',
menu,
status: {
message: 'Error text',
onClick: action('ErrorIndicator: onClick fired'),
},
statusMessage: 'Error text',
statusMessageOnClick: action('ErrorIndicator: onClick fired'),
})}
{renderPanel('No padding, error loadingState, menu', {
{renderPanel('No padding; has statusMessage, menu', {
padding: 'none',
title: 'Default title',
menu,
loadingState: LoadingState.Error,
statusMessage: 'Error text',
statusMessageOnClick: action('ErrorIndicator: onClick fired'),
})}
{renderPanel('No title, error loadingState, menu', {
{renderPanel('No title, loadingState is Error, no statusMessage, menu', {
menu,
loadingState: LoadingState.Error,
})}
{renderPanel('Streaming loadingState, menu', {
{renderPanel('loadingState is Streaming, menu', {
title: 'Default title',
menu,
loadingState: LoadingState.Streaming,
})}
{renderPanel('Loading loadingState, menu', {
{renderPanel('loadingState is Loading, menu', {
title: 'Default title',
menu,
loadingState: LoadingState.Loading,

View File

@ -1,6 +1,5 @@
import { css, cx } from '@emotion/css';
import { isEmpty } from 'lodash';
import React, { CSSProperties, ReactNode, ReactElement } from 'react';
import React, { CSSProperties, ReactElement, ReactNode } from 'react';
import { GrafanaTheme2, LoadingState } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@ -15,11 +14,6 @@ import { Tooltip } from '../Tooltip';
import { PanelDescription } from './PanelDescription';
import { PanelStatus } from './PanelStatus';
interface Status {
message?: string;
onClick?: (e: React.SyntheticEvent) => void;
}
/**
* @internal
*/
@ -36,7 +30,14 @@ export interface PanelChromeProps {
// dragClass?: string;
hoverHeader?: boolean;
loadingState?: LoadingState;
status?: Status;
/**
* Used to display status message (used for panel errors currently)
*/
statusMessage?: string;
/**
* Handle opening error details view (like inspect / error tab)
*/
statusMessageOnClick?: (e: React.SyntheticEvent) => void;
/** @deprecated in favor of props
* status for errors and loadingState for loading and streaming
* which will serve the same purpose
@ -65,8 +66,9 @@ export function PanelChrome({
// dragClass,
hoverHeader = false,
loadingState,
status,
leftItems = [],
statusMessage,
statusMessageOnClick,
leftItems,
}: PanelChromeProps) {
const theme = useTheme2();
const styles = useStyles2(getStyles);
@ -82,7 +84,7 @@ export function PanelChrome({
titleItems.length > 0 ||
description !== '' ||
loadingState === LoadingState.Streaming ||
leftItems.length > 0);
(leftItems?.length ?? 0) > 0);
const headerHeight = getHeaderHeight(theme, hasHeader);
const { contentStyle, innerWidth, innerHeight } = getContentStyle(padding, theme, width, headerHeight, height);
@ -97,29 +99,12 @@ export function PanelChrome({
};
const containerStyles: CSSProperties = { width, height };
const isUsingDeprecatedLeftItems = isEmpty(status) && !loadingState;
const showLoading = loadingState === LoadingState.Loading && !isUsingDeprecatedLeftItems;
const showStreaming = loadingState === LoadingState.Streaming && !isUsingDeprecatedLeftItems;
const renderStatus = () => {
const showError = loadingState === LoadingState.Error || status?.message;
if (!isUsingDeprecatedLeftItems && showError) {
return (
<div className={styles.errorContainer}>
<PanelStatus message={status?.message} onClick={status?.onClick} />
</div>
);
} else {
return null;
}
};
const ariaLabel = title ? selectors.components.Panels.Panel.containerByTitle(title) : 'Panel';
return (
<div className={styles.container} style={containerStyles} aria-label={ariaLabel}>
<div className={styles.loadingBarContainer}>
{showLoading ? <LoadingBar width={'28%'} height={'2px'} /> : null}
{loadingState === LoadingState.Loading ? <LoadingBar width={'28%'} height={'2px'} /> : null}
</div>
<div className={styles.headerContainer} style={headerStyles} data-testid="header-container">
@ -131,13 +116,13 @@ export function PanelChrome({
<PanelDescription description={description} />
{titleItems && (
{titleItems.length > 0 && (
<div className={styles.titleItems} data-testid="title-items-container">
{titleItems.map((item) => item)}
</div>
)}
{showStreaming && (
{loadingState === LoadingState.Streaming && (
<div className={styles.item} style={itemStyles}>
<Tooltip content="Streaming">
<Icon name="circle-mono" size="sm" className={styles.streaming} />
@ -159,11 +144,16 @@ export function PanelChrome({
</Dropdown>
)}
{isUsingDeprecatedLeftItems && <div className={styles.items}>{itemsRenderer(leftItems, (item) => item)}</div>}
{leftItems && <div className={styles.items}>{itemsRenderer(leftItems, (item) => item)}</div>}
</div>
{renderStatus()}
{statusMessage && (
<div className={styles.errorContainer}>
<PanelStatus message={statusMessage} onClick={statusMessageOnClick} />
</div>
)}
</div>
<div className={styles.content} style={contentStyle}>
{children(innerWidth, innerHeight)}
</div>
@ -180,6 +170,7 @@ const getHeaderHeight = (theme: GrafanaTheme2, hasHeader: boolean) => {
if (hasHeader) {
return theme.spacing.gridSize * theme.components.panel.headerHeight;
}
return 0;
};

View File

@ -592,10 +592,11 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
e.stopPropagation();
locationService.partial({ inspect: this.props.panel.id, inspectTab: tab });
};
onOpenErrorInspect(e: React.SyntheticEvent, tab: string) {
onOpenErrorInspect = (e: React.SyntheticEvent) => {
e.stopPropagation();
locationService.partial({ inspect: this.props.panel.id, inspectTab: tab });
}
locationService.partial({ inspect: this.props.panel.id, inspectTab: InspectTab.Error });
};
render() {
const { dashboard, panel, isViewing, isEditing, width, height, plugin } = this.props;
@ -648,10 +649,8 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
height={height}
title={title}
loadingState={data.state}
status={{
message: errorMessage,
onClick: (e: React.SyntheticEvent) => this.onOpenErrorInspect(e, InspectTab.Error),
}}
statusMessage={errorMessage}
statusMessageOnClick={this.onOpenErrorInspect}
description={!!panel.description ? this.onShowPanelDescription : undefined}
titleItems={titleItems}
menu={menu}