From 08806924d87c0050797fed415f7db451f8d28ee6 Mon Sep 17 00:00:00 2001 From: Polina Boneva <13227501+polibb@users.noreply.github.com> Date: Wed, 25 Jan 2023 18:29:53 +0200 Subject: [PATCH] [Chore] Add unit tests to PanelChrome component (#61695) * PanelChrome: test loadingState and status * add some tests * fix error-related tests * clean up * fix tests * pass aria-label from PanelChrome to ToolbarButton * add prop comments and clean up --- .../PanelChrome/PanelChrome.test.tsx | 79 ++++++++++++++++--- .../components/PanelChrome/PanelChrome.tsx | 31 +++++--- .../components/PanelChrome/PanelStatus.tsx | 4 +- 3 files changed, 93 insertions(+), 21 deletions(-) diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx index 2d2169e4812..68eaeebc3f9 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx @@ -1,6 +1,8 @@ import { screen, render } from '@testing-library/react'; import React from 'react'; +import { LoadingState } from '@grafana/data'; + import { PanelChrome, PanelChromeProps } from './PanelChrome'; const setup = (propOverrides?: Partial) => { @@ -35,18 +37,50 @@ it('renders an empty panel with padding', () => { expect(screen.getByText("Panel's Content").parentElement).not.toHaveStyle({ padding: '0px' }); }); -it('renders panel with a header if prop title', () => { +// Check for backwards compatibility +it('renders panel header if prop title', () => { setup({ title: 'Test Panel Header' }); expect(screen.getByTestId('header-container')).toBeInTheDocument(); }); -it('renders panel with a header with title in place if prop title', () => { +// Check for backwards compatibility +it('renders panel with title in place if prop title', () => { setup({ title: 'Test Panel Header' }); expect(screen.getByText('Test Panel Header')).toBeInTheDocument(); }); +// Check for backwards compatibility +it('renders panel with a header if prop leftItems', () => { + setup({ + leftItems: [
This should be a self-contained node
], + }); + + expect(screen.getByTestId('header-container')).toBeInTheDocument(); +}); + +// todo implement when hoverHeader is implemented +it.skip('renders panel without header if no title, no leftItems, and hoverHeader is undefined', () => { + setup(); + + expect(screen.getByTestId('header-container')).toBeInTheDocument(); +}); + +// todo implement when hoverHeader is implemented +it.skip('renders panel with a fixed header if prop hoverHeader is false', () => { + setup({ hoverHeader: false }); + + expect(screen.getByTestId('header-container')).toBeInTheDocument(); +}); + +// todo implement when hoverHeader is implemented +it.skip('renders panel with a hovering header if prop hoverHeader is true', () => { + setup({ title: 'Test Panel Header', hoverHeader: true }); + + expect(screen.queryByTestId('header-container')).not.toBeInTheDocument(); +}); + it('renders panel with a header if prop titleItems', () => { setup({ titleItems: [
This should be a self-contained node
], @@ -63,11 +97,6 @@ it('renders panel with a header with icons in place if prop titleItems', () => { expect(screen.getByTestId('title-items-container')).toBeInTheDocument(); }); -it.skip('renders panel with a fixed header if prop hoverHeader is false', () => { - // setup({ title: 'Test Panel Header', hoverHeader: false }); - // expect(screen.getByTestId('header-container')).toBeInTheDocument(); -}); - it('renders panel with a header if prop menu', () => { setup({ menu:
Menu
}); @@ -81,8 +110,38 @@ it('renders panel with a show-on-hover menu icon if prop menu', () => { expect(screen.getByTestId('panel-menu-button')).not.toBeVisible(); }); -it.skip('renders states in the panel header if any given', () => {}); +it('renders error status in the panel header if any given', () => { + setup({ statusMessage: 'Error test' }); -it.skip('renders leftItems in the panel header if any given when no states prop is given', () => {}); + expect(screen.getByLabelText('Panel status')).toBeInTheDocument(); +}); -it.skip('renders states in the panel header if both leftItems and states are given', () => {}); +it('does not render error status in the panel header if loadingState is error, but no statusMessage', () => { + setup({ loadingState: LoadingState.Error, statusMessage: '' }); + + expect(screen.queryByTestId('panel-status')).not.toBeInTheDocument(); +}); + +it('renders loading indicator in the panel header if loadingState is loading', () => { + setup({ loadingState: LoadingState.Loading }); + + expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument(); +}); + +it('renders loading indicator in the panel header if loadingState is loading regardless of not having a header', () => { + setup({ loadingState: LoadingState.Loading, hoverHeader: true }); + + expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument(); +}); + +it('renders loading indicator in the panel header if loadingState is loading regardless of having a header', () => { + setup({ loadingState: LoadingState.Loading, hoverHeader: false }); + + expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument(); +}); + +it('renders streaming indicator in the panel header if loadingState is streaming', () => { + setup({ loadingState: LoadingState.Streaming }); + + expect(screen.getByTestId('panel-streaming')).toBeInTheDocument(); +}); diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx index 9f91c0cc315..5ea484bbf2b 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx @@ -26,10 +26,13 @@ export interface PanelChromeProps { description?: string | (() => string); titleItems?: ReactNode[]; menu?: ReactElement | (() => ReactElement); - /** dragClass, hoverHeader not yet implemented */ dragClass?: string; dragClassCancel?: string; hoverHeader?: boolean; + /** + * Use only to indicate loading or streaming data in the panel. + * Any other values of loadingState are ignored. + */ loadingState?: LoadingState; /** * Used to display status message (used for panel errors currently) @@ -39,11 +42,13 @@ export interface PanelChromeProps { * 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 + /** + * @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 data state - * */ + * of showing/interacting with the panel's state + */ leftItems?: ReactNode[]; } @@ -64,14 +69,13 @@ export function PanelChrome({ description = '', titleItems = [], menu, - // dragClass, + dragClass, + dragClassCancel, hoverHeader = false, loadingState, statusMessage, statusMessageOnClick, leftItems, - dragClass, - dragClassCancel, }: PanelChromeProps) { const theme = useTheme2(); const styles = useStyles2(getStyles); @@ -108,7 +112,9 @@ export function PanelChrome({ return (
- {loadingState === LoadingState.Loading ? : null} + {loadingState === LoadingState.Loading ? ( + + ) : null}
@@ -127,7 +133,7 @@ export function PanelChrome({ )} {loadingState === LoadingState.Streaming && ( -
+
@@ -156,6 +162,7 @@ export function PanelChrome({ className={cx(styles.errorContainer, dragClassCancel)} message={statusMessage} onClick={statusMessageOnClick} + ariaLabel="Panel status" /> )}
@@ -229,6 +236,7 @@ const getStyles = (theme: GrafanaTheme2) => { }, }), loadingBarContainer: css({ + label: 'panel-loading-bar-container', position: 'absolute', top: 0, width: '100%', @@ -246,6 +254,7 @@ const getStyles = (theme: GrafanaTheme2) => { padding: theme.spacing(0, 0, 0, 1), }), streaming: css({ + label: 'panel-streaming', marginRight: 0, color: theme.colors.success.text, @@ -254,6 +263,7 @@ const getStyles = (theme: GrafanaTheme2) => { }, }), title: css({ + label: 'panel-title', marginBottom: 0, // override default h6 margin-bottom textOverflow: 'ellipsis', overflow: 'hidden', @@ -270,6 +280,7 @@ const getStyles = (theme: GrafanaTheme2) => { alignItems: 'center', }), menuItem: css({ + label: 'panel-menu', visibility: 'hidden', border: 'none', }), diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx index 3a61ca6ec7b..15bbc4a3a08 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx @@ -10,9 +10,10 @@ export interface Props { className?: string; message?: string; onClick?: (e: React.SyntheticEvent) => void; + ariaLabel?: string; } -export function PanelStatus({ className, message, onClick }: Props) { +export function PanelStatus({ className, message, onClick, ariaLabel = 'status' }: Props) { const styles = useStyles2(getStyles); return ( @@ -22,6 +23,7 @@ export function PanelStatus({ className, message, onClick }: Props) { variant={'destructive'} icon="exclamation-triangle" tooltip={message || ''} + aria-label={ariaLabel} /> ); }