Skip to content

Commit c6d0719

Browse files
Chore: Migrate DashboardRow SCSS styles (grafana#88768)
migrate dashboard-row styles
1 parent 372c9d4 commit c6d0719

File tree

4 files changed

+134
-112
lines changed

4 files changed

+134
-112
lines changed

public/app/features/dashboard/components/DashboardRow/DashboardRow.test.tsx

+11-9
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { screen, render } from '@testing-library/react';
22
import userEvent from '@testing-library/user-event';
33
import React from 'react';
44

5+
import { createTheme } from '@grafana/data';
6+
import { selectors } from '@grafana/e2e-selectors';
57
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
68

79
import { PanelModel } from '../../state/PanelModel';
810

9-
import { DashboardRow } from './DashboardRow';
11+
import { DashboardRow, UnthemedDashboardRow } from './DashboardRow';
1012

1113
describe('DashboardRow', () => {
1214
let panel: PanelModel, dashboardMock: any;
@@ -25,18 +27,18 @@ describe('DashboardRow', () => {
2527
panel = new PanelModel({ collapsed: false });
2628
});
2729

28-
it('Should not have collapsed class when collaped is false', () => {
30+
it('Should correctly show expanded state when the panel is expanded', () => {
2931
render(<DashboardRow panel={panel} dashboard={dashboardMock} />);
30-
const row = screen.getByTestId('dashboard-row-container');
32+
const row = screen.getByTestId(selectors.components.DashboardRow.title(''));
3133
expect(row).toBeInTheDocument();
32-
expect(row).not.toHaveClass('dashboard-row--collapsed');
34+
expect(row).toHaveAttribute('aria-expanded', 'true');
3335
});
3436

35-
it('Should collapse when the panel is collapsed', async () => {
37+
it('Should correctly show expanded state when the panel is collapsed', async () => {
3638
const panel = new PanelModel({ collapsed: true });
3739
render(<DashboardRow panel={panel} dashboard={dashboardMock} />);
38-
const row = screen.getByTestId('dashboard-row-container');
39-
expect(row).toHaveClass('dashboard-row--collapsed');
40+
const row = screen.getByTestId(selectors.components.DashboardRow.title(''));
41+
expect(row).toHaveAttribute('aria-expanded', 'false');
4042
});
4143

4244
it('Should collapse after clicking title', async () => {
@@ -79,7 +81,7 @@ describe('DashboardRow', () => {
7981
},
8082
});
8183
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
82-
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
84+
const dashboardRow = new UnthemedDashboardRow({ panel: rowPanel, dashboard: dashboardMock, theme: createTheme() });
8385
expect(dashboardRow.getWarning()).toBeDefined();
8486
});
8587

@@ -91,7 +93,7 @@ describe('DashboardRow', () => {
9193
},
9294
});
9395
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
94-
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
96+
const dashboardRow = new UnthemedDashboardRow({ panel: rowPanel, dashboard: dashboardMock, theme: createTheme() });
9597
expect(dashboardRow.getWarning()).not.toBeDefined();
9698
});
9799
});

public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx

+123-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import classNames from 'classnames';
1+
import { css, cx } from '@emotion/css';
22
import { indexOf } from 'lodash';
33
import React from 'react';
44
import { Unsubscribable } from 'rxjs';
55

6+
import { GrafanaTheme2 } from '@grafana/data';
67
import { selectors } from '@grafana/e2e-selectors';
78
import { getTemplateSrv, RefreshEvent } from '@grafana/runtime';
8-
import { Icon, TextLink } from '@grafana/ui';
9+
import { Icon, TextLink, Themeable2, withTheme2 } from '@grafana/ui';
910
import appEvents from 'app/core/app_events';
1011
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
1112

@@ -14,12 +15,12 @@ import { DashboardModel } from '../../state/DashboardModel';
1415
import { PanelModel } from '../../state/PanelModel';
1516
import { RowOptionsButton } from '../RowOptions/RowOptionsButton';
1617

17-
export interface DashboardRowProps {
18+
export interface DashboardRowProps extends Themeable2 {
1819
panel: PanelModel;
1920
dashboard: DashboardModel;
2021
}
2122

22-
export class DashboardRow extends React.Component<DashboardRowProps> {
23+
export class UnthemedDashboardRow extends React.Component<DashboardRowProps> {
2324
sub?: Unsubscribable;
2425

2526
componentDidMount() {
@@ -93,32 +94,39 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
9394
};
9495

9596
render() {
96-
const classes = classNames({
97-
'dashboard-row': true,
98-
'dashboard-row--collapsed': this.props.panel.collapsed,
99-
});
100-
10197
const title = getTemplateSrv().replace(this.props.panel.title, this.props.panel.scopedVars, 'text');
10298
const count = this.props.panel.panels ? this.props.panel.panels.length : 0;
10399
const panels = count === 1 ? 'panel' : 'panels';
104100
const canEdit = this.props.dashboard.meta.canEdit === true;
101+
const collapsed = this.props.panel.collapsed;
102+
const styles = getStyles(this.props.theme);
105103

106104
return (
107-
<div className={classes} data-testid="dashboard-row-container">
105+
<div
106+
className={cx(styles.dashboardRow, {
107+
[styles.dashboardRowCollapsed]: collapsed,
108+
})}
109+
data-testid="dashboard-row-container"
110+
>
108111
<button
109-
className="dashboard-row__title pointer"
112+
aria-expanded={!collapsed}
113+
className={cx(styles.title, 'pointer')}
110114
type="button"
111115
data-testid={selectors.components.DashboardRow.title(title)}
112116
onClick={this.onToggle}
113117
>
114-
<Icon name={this.props.panel.collapsed ? 'angle-right' : 'angle-down'} />
118+
<Icon name={collapsed ? 'angle-right' : 'angle-down'} />
115119
{title}
116-
<span className="dashboard-row__panel_count">
120+
<span
121+
className={cx(styles.count, {
122+
[styles.countCollapsed]: collapsed,
123+
})}
124+
>
117125
({count} {panels})
118126
</span>
119127
</button>
120128
{canEdit && (
121-
<div className="dashboard-row__actions">
129+
<div className={styles.actions}>
122130
<RowOptionsButton
123131
title={this.props.panel.title}
124132
repeat={this.props.panel.repeat}
@@ -130,16 +138,114 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
130138
</button>
131139
</div>
132140
)}
133-
{this.props.panel.collapsed === true && (
141+
{collapsed === true && (
134142
/* disabling the a11y rules here as the button handles keyboard interactions */
135143
/* this is just to provide a better experience for mouse users */
136144
/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
137-
<div className="dashboard-row__toggle-target" onClick={this.onToggle}>
145+
<div
146+
className={cx({
147+
[styles.toggleTargetCollapsed]: collapsed,
148+
})}
149+
onClick={this.onToggle}
150+
>
138151
&nbsp;
139152
</div>
140153
)}
141-
{canEdit && <div data-testid="dashboard-row-drag" className="dashboard-row__drag grid-drag-handle" />}
154+
{canEdit && (
155+
<div
156+
data-testid="dashboard-row-drag"
157+
className={cx(styles.dragHandle, 'grid-drag-handle', {
158+
[styles.dragHandleCollapsed]: collapsed,
159+
})}
160+
/>
161+
)}
142162
</div>
143163
);
144164
}
145165
}
166+
167+
export const DashboardRow = withTheme2(UnthemedDashboardRow);
168+
169+
const getStyles = (theme: GrafanaTheme2) => {
170+
const actions = css({
171+
color: theme.colors.text.secondary,
172+
opacity: 0,
173+
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
174+
transition: '200ms opacity ease-in 200ms',
175+
},
176+
177+
button: {
178+
color: theme.colors.text.secondary,
179+
paddingLeft: theme.spacing(2),
180+
background: 'transparent',
181+
border: 'none',
182+
183+
'&:hover': {
184+
color: theme.colors.text.maxContrast,
185+
},
186+
},
187+
});
188+
189+
return {
190+
dashboardRow: css({
191+
display: 'flex',
192+
alignItems: 'center',
193+
height: '100%',
194+
195+
'&:hover, &:focus-within': {
196+
[`.${actions}`]: {
197+
opacity: 1,
198+
},
199+
},
200+
}),
201+
dashboardRowCollapsed: css({
202+
background: theme.components.panel.background,
203+
}),
204+
toggleTargetCollapsed: css({
205+
flex: 1,
206+
cursor: 'pointer',
207+
marginRight: '15px',
208+
}),
209+
title: css({
210+
flexGrow: 0,
211+
fontSize: theme.typography.h5.fontSize,
212+
fontWeight: theme.typography.fontWeightMedium,
213+
color: theme.colors.text.primary,
214+
background: 'transparent',
215+
border: 'none',
216+
217+
'.fa': {
218+
color: theme.colors.text.secondary,
219+
fontSize: theme.typography.size.xs,
220+
padding: theme.spacing(0, 1),
221+
},
222+
}),
223+
actions,
224+
count: css({
225+
paddingLeft: theme.spacing(2),
226+
color: theme.colors.text.secondary,
227+
fontStyle: 'italic',
228+
fontSize: theme.typography.size.sm,
229+
fontWeight: 'normal',
230+
display: 'none',
231+
}),
232+
countCollapsed: css({
233+
display: 'inline-block',
234+
}),
235+
dragHandle: css({
236+
cursor: 'move',
237+
width: '16px',
238+
height: '100%',
239+
background: 'url("public/img/grab_dark.svg") no-repeat 50% 50%',
240+
backgroundSize: '8px',
241+
visibility: 'hidden',
242+
position: 'absolute',
243+
top: 0,
244+
right: 0,
245+
}),
246+
dragHandleCollapsed: css({
247+
visibility: 'visible',
248+
opacity: 1,
249+
}),
250+
};
251+
};

public/sass/_grafana.scss

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
@import 'components/query_editor';
4040
@import 'components/tabbed_view';
4141
@import 'components/query_part';
42-
@import 'components/row';
4342
@import 'components/json_explorer';
4443
@import 'components/dashboard_grid';
4544
@import 'components/add_data_source';

public/sass/components/_row.scss

-85
This file was deleted.

0 commit comments

Comments
 (0)