diff --git a/public/app/features/dashboard/components/DashboardLoading/DashboardFailed.tsx b/public/app/features/dashboard/components/DashboardLoading/DashboardFailed.tsx new file mode 100644 index 00000000000..75f2c39cff3 --- /dev/null +++ b/public/app/features/dashboard/components/DashboardLoading/DashboardFailed.tsx @@ -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 ( +
+ + {getMessageFromError(initError.error)} + +
+ ); +}; + +export const getStyles = (theme: GrafanaTheme) => { + return { + dashboardLoading: css` + height: 60vh; + display: flex; + align-items: center; + justify-content: center; + `, + }; +}; diff --git a/public/app/features/dashboard/components/DashboardLoading/DashboardLoading.tsx b/public/app/features/dashboard/components/DashboardLoading/DashboardLoading.tsx new file mode 100644 index 00000000000..40e168bc5d0 --- /dev/null +++ b/public/app/features/dashboard/components/DashboardLoading/DashboardLoading.tsx @@ -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 ( +
+
+ + + {initPhase} + {' '} + + + + +
+
+ ); +}; + +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}; + `, + }; +}; diff --git a/public/app/features/dashboard/containers/DashboardPage.test.tsx b/public/app/features/dashboard/containers/DashboardPage.test.tsx index ccd43c51974..04371b19b12 100644 --- a/public/app/features/dashboard/containers/DashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.test.tsx @@ -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; + wrapper?: ShallowWrapper; mount: (propOverrides?: Partial) => 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(); + ctx.wrapper = shallow(); }, }; diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index ee521549b80..53b4ecf1304 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -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 { +export interface Props + extends Themeable, + GrafanaRouteComponentProps { initPhase: DashboardInitPhase; isInitSlow: boolean; dashboard: DashboardModel | null; @@ -69,7 +73,7 @@ export interface State { showLoadingState: boolean; } -export class DashboardPage extends PureComponent { +export class UnthemedDashboardPage extends PureComponent { private forceRouteReloadCounter = 0; state: State = this.getCleanState(); @@ -265,45 +269,6 @@ export class DashboardPage extends PureComponent { this.setState({ updateScrollTop: 0 }); }; - cancelVariables = () => { - locationService.push('/'); - }; - - renderSlowInitState() { - return ( -
-
- - - {this.props.initPhase} - {' '} - - - - -
-
- ); - } - - renderInitFailedState() { - const { initError } = this.props; - - if (!initError) { - return null; - } - - return ( -
- - {getMessageFromError(initError.error)} - -
- ); - } - getInspectPanel() { const { dashboard, queryParams } = this.props; @@ -324,12 +289,13 @@ export class DashboardPage extends PureComponent { } 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 ; } return null; @@ -341,7 +307,7 @@ export class DashboardPage extends PureComponent { const kioskMode = getKioskMode(queryParams.kiosk); return ( -
+
{kioskMode !== KioskMode.Full && (
{
)} -
+
{ hideHorizontalTrack={true} updateAfterMountMs={500} > -
- {initError && this.renderInitFailedState()} +
+ {initError && } {!editPanel && kioskMode === KioskMode.Off && (
@@ -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)); diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index 07b2b0a6e87..bff2981e711 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -2,7 +2,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`] = `
-
- - - - - Fetching - - - - - - -
-
+ `; exports[`DashboardPage Given initial state Should render nothing 1`] = `""`; exports[`DashboardPage When dashboard has editview url state should render settings view 1`] = `