DashboardPage: refactored styles from sass to emotion (#32955)

* DashboardPage: refactored styles from sass to emotion

* refactored dashboardPage component to be alot easier to read and understand

* more refactoring...

* more cleaning...

* fixes frontend test

* fixes frontend test- I hope

* fixes frontend test- I hope

* moves dashboard scss styles back to it's standalone file
This commit is contained in:
Uchechukwu Obasi 2021-04-15 11:21:36 +01:00 committed by GitHub
parent d0ad2931fa
commit 99b85d8af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 173 additions and 176 deletions

View File

@ -0,0 +1,36 @@
import React from 'react';
import { css } from 'emotion';
import { Alert, useStyles } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { DashboardInitError, AppNotificationSeverity } from 'app/types';
import { getMessageFromError } from 'app/core/utils/errors';
export interface Props {
initError?: DashboardInitError;
}
export const DashboardFailed = ({ initError }: Props) => {
const styles = useStyles(getStyles);
if (!initError) {
return null;
}
return (
<div className={styles.dashboardLoading}>
<Alert severity={AppNotificationSeverity.Error} title={initError.message}>
{getMessageFromError(initError.error)}
</Alert>
</div>
);
};
export const getStyles = (theme: GrafanaTheme) => {
return {
dashboardLoading: css`
height: 60vh;
display: flex;
align-items: center;
justify-content: center;
`,
};
};

View File

@ -0,0 +1,48 @@
import React from 'react';
import { css } from 'emotion';
import { Button, HorizontalGroup, Spinner, useStyles, VerticalGroup } from '@grafana/ui';
import { locationService } from '@grafana/runtime';
import { GrafanaTheme } from '@grafana/data';
import { DashboardInitPhase } from 'app/types';
export interface Props {
initPhase: DashboardInitPhase;
}
export const DashboardLoading = ({ initPhase }: Props) => {
const styles = useStyles(getStyles);
const cancelVariables = () => {
locationService.push('/');
};
return (
<div className={styles.dashboardLoading}>
<div className={styles.dashboardLoadingText}>
<VerticalGroup spacing="md">
<HorizontalGroup align="center" justify="center" spacing="xs">
<Spinner inline={true} /> {initPhase}
</HorizontalGroup>{' '}
<HorizontalGroup align="center" justify="center">
<Button variant="secondary" size="md" icon="repeat" onClick={cancelVariables}>
Cancel loading dashboard
</Button>
</HorizontalGroup>
</VerticalGroup>
</div>
</div>
);
};
export const getStyles = (theme: GrafanaTheme) => {
return {
dashboardLoading: css`
height: 60vh;
display: flex;
align-items: center;
justify-content: center;
`,
dashboardLoadingText: css`
font-size: ${theme.typography.size.lg};
`,
};
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { DashboardPage, mapStateToProps, Props, State } from './DashboardPage';
import { UnthemedDashboardPage, mapStateToProps, Props, State } from './DashboardPage';
import { DashboardModel } from '../state';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { DashboardInitPhase, DashboardRoutes } from 'app/types';
@ -8,6 +8,7 @@ import { notifyApp } from 'app/core/actions';
import { cleanUpDashboardAndVariables } from '../state/actions';
import { selectors } from '@grafana/e2e-selectors';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { getTheme } from '@grafana/ui';
jest.mock('app/features/dashboard/components/DashboardSettings/GeneralSettings', () => ({}));
@ -15,7 +16,7 @@ interface ScenarioContext {
cleanUpDashboardAndVariablesMock: typeof cleanUpDashboardAndVariables;
dashboard?: DashboardModel | null;
setDashboardProp: (overrides?: any, metaOverrides?: any) => void;
wrapper?: ShallowWrapper<Props, State, DashboardPage>;
wrapper?: ShallowWrapper<Props, State, UnthemedDashboardPage>;
mount: (propOverrides?: Partial<Props>) => void;
setup: (fn: () => void) => void;
}
@ -67,12 +68,13 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
cancelVariables: jest.fn(),
templateVarsChangedInUrl: jest.fn(),
dashboard: null,
theme: getTheme(),
};
Object.assign(props, propOverrides);
ctx.dashboard = props.dashboard;
ctx.wrapper = shallow(<DashboardPage {...props} />);
ctx.wrapper = shallow(<UnthemedDashboardPage {...props} />);
},
};

View File

@ -1,13 +1,13 @@
import $ from 'jquery';
import React, { MouseEvent, PureComponent } from 'react';
import { css } from 'emotion';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { getLegacyAngularInjector, locationService } from '@grafana/runtime';
import { selectors } from '@grafana/e2e-selectors';
import { Alert, Button, CustomScrollbar, HorizontalGroup, Spinner, VerticalGroup } from '@grafana/ui';
import { CustomScrollbar, stylesFactory, withTheme, Themeable } from '@grafana/ui';
import { createErrorNotification } from 'app/core/copy/appNotification';
import { getMessageFromError } from 'app/core/utils/errors';
import { Branding } from 'app/core/components/Branding/Branding';
import { DashboardGrid } from '../dashgrid/DashboardGrid';
import { DashNav } from '../components/DashNav';
@ -15,7 +15,7 @@ import { DashboardSettings } from '../components/DashboardSettings';
import { PanelEditor } from '../components/PanelEditor/PanelEditor';
import { initDashboard } from '../state/initDashboard';
import { notifyApp } from 'app/core/actions';
import { AppNotificationSeverity, DashboardInitError, DashboardInitPhase, KioskMode, StoreState } from 'app/types';
import { DashboardInitError, DashboardInitPhase, KioskMode, StoreState } from 'app/types';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { PanelInspector } from '../components/Inspector/PanelInspector';
import { SubMenu } from '../components/SubMenu/SubMenu';
@ -26,7 +26,9 @@ import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { getTimeSrv } from '../services/TimeSrv';
import { getKioskMode } from 'app/core/navigation/kiosk';
import { UrlQueryValue } from '@grafana/data';
import { GrafanaTheme, UrlQueryValue } from '@grafana/data';
import { DashboardLoading } from '../components/DashboardLoading/DashboardLoading';
import { DashboardFailed } from '../components/DashboardLoading/DashboardFailed';
export interface DashboardPageRouteParams {
uid?: string;
@ -47,7 +49,9 @@ type DashboardPageRouteSearchParams = {
refresh?: string;
};
export interface Props extends GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {
export interface Props
extends Themeable,
GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {
initPhase: DashboardInitPhase;
isInitSlow: boolean;
dashboard: DashboardModel | null;
@ -69,7 +73,7 @@ export interface State {
showLoadingState: boolean;
}
export class DashboardPage extends PureComponent<Props, State> {
export class UnthemedDashboardPage extends PureComponent<Props, State> {
private forceRouteReloadCounter = 0;
state: State = this.getCleanState();
@ -265,45 +269,6 @@ export class DashboardPage extends PureComponent<Props, State> {
this.setState({ updateScrollTop: 0 });
};
cancelVariables = () => {
locationService.push('/');
};
renderSlowInitState() {
return (
<div className="dashboard-loading">
<div className="dashboard-loading__text">
<VerticalGroup spacing="md">
<HorizontalGroup align="center" justify="center" spacing="xs">
<Spinner inline={true} /> {this.props.initPhase}
</HorizontalGroup>{' '}
<HorizontalGroup align="center" justify="center">
<Button variant="secondary" size="md" icon="repeat" onClick={this.cancelVariables}>
Cancel loading dashboard
</Button>
</HorizontalGroup>
</VerticalGroup>
</div>
</div>
);
}
renderInitFailedState() {
const { initError } = this.props;
if (!initError) {
return null;
}
return (
<div className="dashboard-loading">
<Alert severity={AppNotificationSeverity.Error} title={initError.message}>
{getMessageFromError(initError.error)}
</Alert>
</div>
);
}
getInspectPanel() {
const { dashboard, queryParams } = this.props;
@ -324,12 +289,13 @@ export class DashboardPage extends PureComponent<Props, State> {
}
render() {
const { dashboard, isInitSlow, initError, isPanelEditorOpen, queryParams } = this.props;
const { dashboard, isInitSlow, initError, isPanelEditorOpen, queryParams, theme } = this.props;
const { editPanel, viewPanel, scrollTop, updateScrollTop } = this.state;
const styles = getStyles(theme);
if (!dashboard) {
if (isInitSlow) {
return this.renderSlowInitState();
return <DashboardLoading initPhase={this.props.initPhase} />;
}
return null;
@ -341,7 +307,7 @@ export class DashboardPage extends PureComponent<Props, State> {
const kioskMode = getKioskMode(queryParams.kiosk);
return (
<div className="dashboard-container">
<div className={styles.dashboardContainer}>
{kioskMode !== KioskMode.Full && (
<div aria-label={selectors.pages.Dashboard.DashNav.nav}>
<DashNav
@ -354,7 +320,7 @@ export class DashboardPage extends PureComponent<Props, State> {
</div>
)}
<div className="dashboard-scroll">
<div className={styles.dashboardScroll}>
<CustomScrollbar
autoHeightMin="100%"
setScrollTop={this.setScrollTop}
@ -362,8 +328,8 @@ export class DashboardPage extends PureComponent<Props, State> {
hideHorizontalTrack={true}
updateAfterMountMs={500}
>
<div className="dashboard-content">
{initError && this.renderInitFailedState()}
<div className={styles.dashboardContent}>
{initError && <DashboardFailed />}
{!editPanel && kioskMode === KioskMode.Off && (
<div aria-label={selectors.pages.Dashboard.SubMenu.submenu}>
<SubMenu dashboard={dashboard} annotations={dashboard.annotations.list} links={dashboard.links} />
@ -405,4 +371,35 @@ const mapDispatchToProps = {
templateVarsChangedInUrl,
};
/*
* Styles
*/
export const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
dashboardContainer: css`
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex: 1 1 0;
flex-direction: column;
`,
dashboardScroll: css`
width: 100%;
flex-grow: 1;
min-height: 0;
display: flex;
`,
dashboardContent: css`
padding: ${theme.spacing.md};
flex-basis: 100%;
flex-grow: 1;
`,
};
});
export const DashboardPage = withTheme(UnthemedDashboardPage);
DashboardPage.displayName = 'DashboardPage';
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));

View File

@ -2,7 +2,7 @@
exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`] = `
<div
className="dashboard-container"
className="css-1mwktlb"
>
<div
aria-label="Dashboard navigation"
@ -108,7 +108,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
/>
</div>
<div
className="dashboard-scroll"
className="css-17x4n39"
>
<CustomScrollbar
autoHeightMin="100%"
@ -117,7 +117,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
updateAfterMountMs={500}
>
<div
className="dashboard-content"
className="css-50qyg5"
>
<div
aria-label="Dashboard submenu"
@ -339,50 +339,16 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
`;
exports[`DashboardPage Dashboard is fetching slowly Should render slow init state 1`] = `
<div
className="dashboard-loading"
>
<div
className="dashboard-loading__text"
>
<VerticalGroup
spacing="md"
>
<HorizontalGroup
align="center"
justify="center"
spacing="xs"
>
<Spinner
inline={true}
/>
Fetching
</HorizontalGroup>
<HorizontalGroup
align="center"
justify="center"
>
<Button
icon="repeat"
onClick={[Function]}
size="md"
variant="secondary"
>
Cancel loading dashboard
</Button>
</HorizontalGroup>
</VerticalGroup>
</div>
</div>
<DashboardLoading
initPhase="Fetching"
/>
`;
exports[`DashboardPage Given initial state Should render nothing 1`] = `""`;
exports[`DashboardPage When dashboard has editview url state should render settings view 1`] = `
<div
className="dashboard-container"
className="css-1mwktlb"
>
<div
aria-label="Dashboard navigation"
@ -488,7 +454,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
/>
</div>
<div
className="dashboard-scroll"
className="css-17x4n39"
>
<CustomScrollbar
autoHeightMin="100%"
@ -497,7 +463,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
updateAfterMountMs={500}
>
<div
className="dashboard-content"
className="css-50qyg5"
>
<div
aria-label="Dashboard submenu"

View File

@ -1,64 +1,7 @@
.dashboard-container {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex: 1 1 0;
flex-direction: column;
}
.dashboard-scroll {
width: 100%;
flex-grow: 1;
min-height: 0;
display: flex;
}
.dashboard-content {
padding: $dashboard-padding;
flex-basis: 100%;
flex-grow: 1;
}
div.flot-text {
color: $text-color !important;
}
.panel {
height: 100%;
}
.dashboard-solo {
.footer,
.sidemenu {
display: none;
}
}
.template-variable {
color: $variable;
}
.panel-solo {
position: fixed;
bottom: 0;
right: 0;
margin: 0;
left: 0;
top: 0;
.panel-container {
border: none;
}
.panel-menu-toggle,
.panel-menu {
display: none;
}
}
.panel-height-helper {
display: block;
height: 100%;
@ -109,31 +52,36 @@ div.flot-text {
padding: 0;
}
}
.dashboard-header {
font-size: $font-size-h2;
text-align: center;
overflow: hidden;
position: relative;
top: -10px;
span {
display: inline-block;
@include brand-bottom-border();
padding: $space-sm $space-sm $space-xxs $space-sm;
div.flot-text {
color: $text-color !important;
}
.dashboard-solo {
.footer,
.sidemenu {
display: none;
}
}
.dashboard-loading {
height: 60vh;
display: flex;
align-items: center;
justify-content: center;
.template-variable {
color: $variable;
}
.alert {
max-width: 600px;
min-width: 600px;
.panel-solo {
position: fixed;
bottom: 0;
right: 0;
margin: 0;
left: 0;
top: 0;
.panel-container {
border: none;
}
.panel-menu-toggle,
.panel-menu {
display: none;
}
}
.dashboard-loading__text {
font-size: $font-size-lg;
}