Chore: Migrate DashboardRow SCSS styles (#88768)

migrate dashboard-row styles
This commit is contained in:
Ashley Harrison 2024-06-07 11:31:58 +01:00 committed by GitHub
parent 372c9d46d5
commit c6d07194cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 112 deletions

View File

@ -2,11 +2,13 @@ import { screen, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { createTheme } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
import { PanelModel } from '../../state/PanelModel';
import { DashboardRow } from './DashboardRow';
import { DashboardRow, UnthemedDashboardRow } from './DashboardRow';
describe('DashboardRow', () => {
let panel: PanelModel, dashboardMock: any;
@ -25,18 +27,18 @@ describe('DashboardRow', () => {
panel = new PanelModel({ collapsed: false });
});
it('Should not have collapsed class when collaped is false', () => {
it('Should correctly show expanded state when the panel is expanded', () => {
render(<DashboardRow panel={panel} dashboard={dashboardMock} />);
const row = screen.getByTestId('dashboard-row-container');
const row = screen.getByTestId(selectors.components.DashboardRow.title(''));
expect(row).toBeInTheDocument();
expect(row).not.toHaveClass('dashboard-row--collapsed');
expect(row).toHaveAttribute('aria-expanded', 'true');
});
it('Should collapse when the panel is collapsed', async () => {
it('Should correctly show expanded state when the panel is collapsed', async () => {
const panel = new PanelModel({ collapsed: true });
render(<DashboardRow panel={panel} dashboard={dashboardMock} />);
const row = screen.getByTestId('dashboard-row-container');
expect(row).toHaveClass('dashboard-row--collapsed');
const row = screen.getByTestId(selectors.components.DashboardRow.title(''));
expect(row).toHaveAttribute('aria-expanded', 'false');
});
it('Should collapse after clicking title', async () => {
@ -79,7 +81,7 @@ describe('DashboardRow', () => {
},
});
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
const dashboardRow = new UnthemedDashboardRow({ panel: rowPanel, dashboard: dashboardMock, theme: createTheme() });
expect(dashboardRow.getWarning()).toBeDefined();
});
@ -91,7 +93,7 @@ describe('DashboardRow', () => {
},
});
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
const dashboardRow = new UnthemedDashboardRow({ panel: rowPanel, dashboard: dashboardMock, theme: createTheme() });
expect(dashboardRow.getWarning()).not.toBeDefined();
});
});

View File

@ -1,11 +1,12 @@
import classNames from 'classnames';
import { css, cx } from '@emotion/css';
import { indexOf } from 'lodash';
import React from 'react';
import { Unsubscribable } from 'rxjs';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { getTemplateSrv, RefreshEvent } from '@grafana/runtime';
import { Icon, TextLink } from '@grafana/ui';
import { Icon, TextLink, Themeable2, withTheme2 } from '@grafana/ui';
import appEvents from 'app/core/app_events';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
@ -14,12 +15,12 @@ import { DashboardModel } from '../../state/DashboardModel';
import { PanelModel } from '../../state/PanelModel';
import { RowOptionsButton } from '../RowOptions/RowOptionsButton';
export interface DashboardRowProps {
export interface DashboardRowProps extends Themeable2 {
panel: PanelModel;
dashboard: DashboardModel;
}
export class DashboardRow extends React.Component<DashboardRowProps> {
export class UnthemedDashboardRow extends React.Component<DashboardRowProps> {
sub?: Unsubscribable;
componentDidMount() {
@ -93,32 +94,39 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
};
render() {
const classes = classNames({
'dashboard-row': true,
'dashboard-row--collapsed': this.props.panel.collapsed,
});
const title = getTemplateSrv().replace(this.props.panel.title, this.props.panel.scopedVars, 'text');
const count = this.props.panel.panels ? this.props.panel.panels.length : 0;
const panels = count === 1 ? 'panel' : 'panels';
const canEdit = this.props.dashboard.meta.canEdit === true;
const collapsed = this.props.panel.collapsed;
const styles = getStyles(this.props.theme);
return (
<div className={classes} data-testid="dashboard-row-container">
<div
className={cx(styles.dashboardRow, {
[styles.dashboardRowCollapsed]: collapsed,
})}
data-testid="dashboard-row-container"
>
<button
className="dashboard-row__title pointer"
aria-expanded={!collapsed}
className={cx(styles.title, 'pointer')}
type="button"
data-testid={selectors.components.DashboardRow.title(title)}
onClick={this.onToggle}
>
<Icon name={this.props.panel.collapsed ? 'angle-right' : 'angle-down'} />
<Icon name={collapsed ? 'angle-right' : 'angle-down'} />
{title}
<span className="dashboard-row__panel_count">
<span
className={cx(styles.count, {
[styles.countCollapsed]: collapsed,
})}
>
({count} {panels})
</span>
</button>
{canEdit && (
<div className="dashboard-row__actions">
<div className={styles.actions}>
<RowOptionsButton
title={this.props.panel.title}
repeat={this.props.panel.repeat}
@ -130,16 +138,114 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
</button>
</div>
)}
{this.props.panel.collapsed === true && (
{collapsed === true && (
/* disabling the a11y rules here as the button handles keyboard interactions */
/* this is just to provide a better experience for mouse users */
/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
<div className="dashboard-row__toggle-target" onClick={this.onToggle}>
<div
className={cx({
[styles.toggleTargetCollapsed]: collapsed,
})}
onClick={this.onToggle}
>
&nbsp;
</div>
)}
{canEdit && <div data-testid="dashboard-row-drag" className="dashboard-row__drag grid-drag-handle" />}
{canEdit && (
<div
data-testid="dashboard-row-drag"
className={cx(styles.dragHandle, 'grid-drag-handle', {
[styles.dragHandleCollapsed]: collapsed,
})}
/>
)}
</div>
);
}
}
export const DashboardRow = withTheme2(UnthemedDashboardRow);
const getStyles = (theme: GrafanaTheme2) => {
const actions = css({
color: theme.colors.text.secondary,
opacity: 0,
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
transition: '200ms opacity ease-in 200ms',
},
button: {
color: theme.colors.text.secondary,
paddingLeft: theme.spacing(2),
background: 'transparent',
border: 'none',
'&:hover': {
color: theme.colors.text.maxContrast,
},
},
});
return {
dashboardRow: css({
display: 'flex',
alignItems: 'center',
height: '100%',
'&:hover, &:focus-within': {
[`.${actions}`]: {
opacity: 1,
},
},
}),
dashboardRowCollapsed: css({
background: theme.components.panel.background,
}),
toggleTargetCollapsed: css({
flex: 1,
cursor: 'pointer',
marginRight: '15px',
}),
title: css({
flexGrow: 0,
fontSize: theme.typography.h5.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.primary,
background: 'transparent',
border: 'none',
'.fa': {
color: theme.colors.text.secondary,
fontSize: theme.typography.size.xs,
padding: theme.spacing(0, 1),
},
}),
actions,
count: css({
paddingLeft: theme.spacing(2),
color: theme.colors.text.secondary,
fontStyle: 'italic',
fontSize: theme.typography.size.sm,
fontWeight: 'normal',
display: 'none',
}),
countCollapsed: css({
display: 'inline-block',
}),
dragHandle: css({
cursor: 'move',
width: '16px',
height: '100%',
background: 'url("public/img/grab_dark.svg") no-repeat 50% 50%',
backgroundSize: '8px',
visibility: 'hidden',
position: 'absolute',
top: 0,
right: 0,
}),
dragHandleCollapsed: css({
visibility: 'visible',
opacity: 1,
}),
};
};

View File

@ -39,7 +39,6 @@
@import 'components/query_editor';
@import 'components/tabbed_view';
@import 'components/query_part';
@import 'components/row';
@import 'components/json_explorer';
@import 'components/dashboard_grid';
@import 'components/add_data_source';

View File

@ -1,85 +0,0 @@
.dashboard-row {
display: flex;
align-items: center;
height: 100%;
&--collapsed {
background: $panel-bg;
.dashboard-row__panel_count {
display: inline-block;
}
.dashboard-row__drag {
visibility: visible;
opacity: 1;
}
.dashboard-row__toggle-target {
flex: 1;
cursor: pointer;
margin-right: 15px;
}
}
&:hover,
&:focus-within {
.dashboard-row__actions {
opacity: 1;
}
}
}
.dashboard-row__title {
flex-grow: 0;
font-size: $font-size-h5;
font-weight: $font-weight-semi-bold;
color: $text-color;
background: transparent;
border: none;
.fa {
color: $text-muted;
font-size: $font-size-xs;
padding: 0 $space-sm;
}
}
.dashboard-row__actions {
color: $text-muted;
opacity: 0;
transition: 200ms opacity ease-in 200ms;
button {
color: $text-color-weak;
padding-left: $spacer;
background: transparent;
border: none;
&:hover {
color: $link-hover-color;
}
}
}
.dashboard-row__panel_count {
padding-left: $spacer;
color: $text-color-weak;
font-style: italic;
font-size: $font-size-sm;
font-weight: normal;
display: none;
}
.dashboard-row__drag {
cursor: move;
width: 16px;
height: 100%;
background: url('../img/grab_dark.svg') no-repeat 50% 50%;
background-size: 8px;
visibility: hidden;
position: absolute;
top: 0;
right: 0;
}