[Uptime] Use EUI color palette for charts/histograms (#29439) (#30178)

* Add hardcoded eui primary/danger to charts/histograms.

* Use EUI JSON vars for color values.
This commit is contained in:
Justin Kambic 2019-02-06 06:53:54 -05:00 committed by GitHub
parent 3d565603d0
commit 6acd31f085
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 210 additions and 161 deletions

View file

@ -127,6 +127,7 @@ exports[`Snapshot component renders without errors 1`] = `
paddingSize="s"
>
<Component
dangerColor="danger"
histogram={
Array [
Object {
@ -424,6 +425,7 @@ exports[`Snapshot component renders without errors 1`] = `
},
]
}
primaryColor="primary"
/>
</EuiPanel>
</EuiFlexItem>

View file

@ -163,7 +163,14 @@ describe('MonitorList component', () => {
it('renders a monitor list without errors', () => {
const { monitors } = monitorResult;
const component = shallowWithIntl(<MonitorList loading={false} monitors={monitors || []} />);
const component = shallowWithIntl(
<MonitorList
dangerColor="danger"
loading={false}
monitors={monitors || []}
primaryColor="primary"
/>
);
expect(component).toMatchSnapshot();
});
});

View file

@ -80,7 +80,9 @@ describe('Snapshot component', () => {
it('renders without errors', () => {
const { snapshot } = data;
const wrapper = shallowWithIntl(<Snapshot snapshot={snapshot} />);
const wrapper = shallowWithIntl(
<Snapshot danger="danger" primary="primary" snapshot={snapshot} />
);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -26,8 +26,10 @@ import { LatestMonitor } from '../../../common/graphql/types';
import { formatSparklineCounts } from './format_sparkline_counts';
interface MonitorListProps {
dangerColor: string;
loading: boolean;
monitors: LatestMonitor[];
primaryColor: string;
}
const MONITOR_LIST_DEFAULT_PAGINATION = 10;
@ -37,97 +39,7 @@ const monitorListPagination = {
pageSizeOptions: [5, 10, 20, 50],
};
const monitorListColumns = [
{
field: 'ping.monitor.status',
name: i18n.translate('xpack.uptime.monitorList.statusColumnLabel', {
defaultMessage: 'Status',
}),
render: (status: string) => (
<EuiHealth color={status === 'up' ? 'success' : 'danger'}>
{status === 'up'
? i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', {
defaultMessage: 'Up',
})
: i18n.translate('xpack.uptime.monitorList.statusColumn.downLabel', {
defaultMessage: 'Down',
})}
</EuiHealth>
),
sortable: true,
},
{
field: 'ping.timestamp',
name: i18n.translate('xpack.uptime.monitorList.lastUpdatedColumnLabel', {
defaultMessage: 'Last updated',
}),
render: (timestamp: string) => moment(timestamp).fromNow(),
sortable: true,
},
{
field: 'ping.monitor.host',
name: i18n.translate('xpack.uptime.monitorList.hostColumnLabel', {
defaultMessage: 'Host',
}),
render: (host: string, monitor: any) => <Link to={`/monitor/${monitor.key.id}`}>{host}</Link>,
},
{
field: 'key.port',
name: i18n.translate('xpack.uptime.monitorList.portColumnLabel', {
defaultMessage: 'Port',
}),
sortable: true,
},
{
field: 'ping.monitor.type',
name: i18n.translate('xpack.uptime.monitorList.typeColumnLabel', {
defaultMessage: 'Type',
}),
sortable: true,
},
{
field: 'ping.monitor.ip',
name: i18n.translate('xpack.uptime.monitorList.ipColumnLabel', { defaultMessage: 'IP' }),
sortable: true,
},
{
field: 'upSeries',
name: i18n.translate('xpack.uptime.monitorList.monitorHistoryColumnLabel', {
defaultMessage: 'Monitor History',
}),
// @ts-ignore TODO fix typing
render: (upSeries, monitor) => {
const { downSeries } = monitor;
return (
<EuiSeriesChart
showDefaultAxis={false}
height={70}
stackBy="y"
// TODO: style hack
style={{ marginBottom: '-20px' }}
xType={EuiSeriesChartUtils.SCALE.TIME}
>
<EuiHistogramSeries
data={formatSparklineCounts(upSeries)}
name={i18n.translate('xpack.uptime.monitorList.upLineSeries.upLabel', {
defaultMessage: 'Up',
})}
color="green"
/>
<EuiHistogramSeries
data={formatSparklineCounts(downSeries)}
name={i18n.translate('xpack.uptime.monitorList.downLineSeries.downLabel', {
defaultMessage: 'Down',
})}
color="red"
/>
</EuiSeriesChart>
);
},
},
];
export const MonitorList = ({ loading, monitors }: MonitorListProps) => (
export const MonitorList = ({ dangerColor, loading, monitors, primaryColor }: MonitorListProps) => (
<Fragment>
<EuiTitle size="xs">
<h5>
@ -139,7 +51,99 @@ export const MonitorList = ({ loading, monitors }: MonitorListProps) => (
</EuiTitle>
<EuiPanel paddingSize="l">
<EuiInMemoryTable
columns={monitorListColumns}
columns={[
{
field: 'ping.monitor.status',
name: i18n.translate('xpack.uptime.monitorList.statusColumnLabel', {
defaultMessage: 'Status',
}),
render: (status: string) => (
<EuiHealth color={status === 'up' ? 'success' : 'danger'}>
{status === 'up'
? i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', {
defaultMessage: 'Up',
})
: i18n.translate('xpack.uptime.monitorList.statusColumn.downLabel', {
defaultMessage: 'Down',
})}
</EuiHealth>
),
sortable: true,
},
{
field: 'ping.timestamp',
name: i18n.translate('xpack.uptime.monitorList.lastUpdatedColumnLabel', {
defaultMessage: 'Last updated',
}),
render: (timestamp: string) => moment(timestamp).fromNow(),
sortable: true,
},
{
field: 'ping.monitor.host',
name: i18n.translate('xpack.uptime.monitorList.hostColumnLabel', {
defaultMessage: 'Host',
}),
render: (host: string, monitor: any) => (
<Link to={`/monitor/${monitor.key.id}`}>{host}</Link>
),
},
{
field: 'key.port',
name: i18n.translate('xpack.uptime.monitorList.portColumnLabel', {
defaultMessage: 'Port',
}),
sortable: true,
},
{
field: 'ping.monitor.type',
name: i18n.translate('xpack.uptime.monitorList.typeColumnLabel', {
defaultMessage: 'Type',
}),
sortable: true,
},
{
field: 'ping.monitor.ip',
name: i18n.translate('xpack.uptime.monitorList.ipColumnLabel', {
defaultMessage: 'IP',
}),
sortable: true,
},
{
field: 'upSeries',
name: i18n.translate('xpack.uptime.monitorList.monitorHistoryColumnLabel', {
defaultMessage: 'Monitor History',
}),
// @ts-ignore TODO fix typing
render: (upSeries, monitor) => {
const { downSeries } = monitor;
return (
<EuiSeriesChart
showDefaultAxis={false}
height={70}
stackBy="y"
// TODO: style hack
style={{ marginBottom: '-20px' }}
xType={EuiSeriesChartUtils.SCALE.TIME}
>
<EuiHistogramSeries
data={formatSparklineCounts(downSeries)}
name={i18n.translate('xpack.uptime.monitorList.downLineSeries.downLabel', {
defaultMessage: 'Down',
})}
color={dangerColor}
/>
<EuiHistogramSeries
data={formatSparklineCounts(upSeries)}
name={i18n.translate('xpack.uptime.monitorList.upLineSeries.upLabel', {
defaultMessage: 'Up',
})}
color={primaryColor}
/>
</EuiSeriesChart>
);
},
},
]}
loading={loading}
items={monitors}
pagination={monitorListPagination}

View file

@ -29,10 +29,16 @@ import { Snapshot as SnapshotType } from '../../../common/graphql/types';
import { SnapshotHistogram } from './snapshot_histogram';
interface SnapshotProps {
danger: string;
primary: string;
snapshot: SnapshotType;
}
export const Snapshot = ({ snapshot: { up, down, total, histogram } }: SnapshotProps) => (
export const Snapshot = ({
danger,
snapshot: { up, down, total, histogram },
primary,
}: SnapshotProps) => (
<EuiFlexGroup alignItems="baseline" gutterSize="xl">
<EuiFlexItem>
<EuiTitle size="xs">
@ -96,7 +102,9 @@ export const Snapshot = ({ snapshot: { up, down, total, histogram } }: SnapshotP
</EuiTitle>
{/* TODO: this is a UI hack that should be replaced */}
<EuiPanel paddingSize="s">
{histogram && <SnapshotHistogram histogram={histogram} />}
{histogram && (
<SnapshotHistogram dangerColor={danger} primaryColor={primary} histogram={histogram} />
)}
{!histogram && (
<EuiEmptyPrompt
title={

View file

@ -12,10 +12,16 @@ import { HistogramSeries } from '../../../common/graphql/types';
import { formatHistogramData } from '../../lib/adapters/monitors/format_histogram_data';
interface SnapshotHistogramProps {
primaryColor: string;
dangerColor: string;
histogram: HistogramSeries[];
}
export const SnapshotHistogram = ({ histogram }: SnapshotHistogramProps) => {
export const SnapshotHistogram = ({
dangerColor,
histogram,
primaryColor,
}: SnapshotHistogramProps) => {
const { upSeriesData, downSeriesData } = formatHistogramData(histogram);
return (
@ -25,14 +31,14 @@ export const SnapshotHistogram = ({ histogram }: SnapshotHistogramProps) => {
name={i18n.translate('xpack.uptime.snapshotHistogram.series.upLabel', {
defaultMessage: 'Up',
})}
color="green"
color={primaryColor}
/>
<EuiHistogramSeries
data={downSeriesData}
name={i18n.translate('xpack.uptime.snapshotHistogram.series.downLabel', {
defaultMessage: 'Down',
})}
color="red"
color={dangerColor}
/>
</EuiSeriesChart>
);

View file

@ -46,6 +46,7 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
public render() {
const {
colors: { primary, secondary, danger },
dateRangeStart,
dateRangeEnd,
monitorId,
@ -74,55 +75,25 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
// TODO: this should not exist in the UI, update the GQL resolver/schema to return
// an object that contains these series already shaped in the way required by the visualizations.
const { monitorChartsData } = data;
const rttWriteRequestSeries: any[] = [];
const rttValidateSeries: any[] = [];
const rttContentSeries: any[] = [];
const rttResponseSeries: any[] = [];
const rttTcpSeries: any[] = [];
const avgDurationSeries: any[] = [];
const areaRttSeries: any[] = [];
const areaDurationSeries: any[] = [];
const downSeries: any[] = [];
const upSeries: any[] = [];
const checksSeries: any[] = [];
const maxRtt: any[] = [];
monitorChartsData.forEach(
({
maxWriteRequest,
maxValidate,
maxContent,
maxResponse,
maxTcpRtt,
avgDuration,
maxDuration,
minDuration,
status,
}: any) => {
// We're summing these values because we need to know what the max value of the RTT
// fields are in order to provide an accurate domain size for the RTT combination series.
maxRtt.push({
x: maxWriteRequest.x,
y: maxWriteRequest.y + maxValidate.y + maxContent.y + maxResponse.y + maxTcpRtt.y,
});
// TODO: these types of computations should take place on the server and be reflected in the GQL schema
rttWriteRequestSeries.push(maxWriteRequest);
rttValidateSeries.push(maxValidate);
rttContentSeries.push(maxContent);
rttResponseSeries.push(maxResponse);
rttTcpSeries.push(maxTcpRtt);
avgDurationSeries.push(avgDuration);
areaRttSeries.push({ x: minDuration.x, y0: minDuration.y, y: maxDuration.y });
downSeries.push({ x: status.x, y: status.down });
upSeries.push({ x: status.x, y: status.up });
checksSeries.push({ x: status.x, y: status.total });
}
);
monitorChartsData.forEach(({ avgDuration, maxDuration, minDuration, status }: any) => {
avgDurationSeries.push(avgDuration);
areaDurationSeries.push({ x: minDuration.x, y0: minDuration.y, y: maxDuration.y });
downSeries.push({ x: status.x, y: status.down });
upSeries.push({ x: status.x, y: status.up });
checksSeries.push({ x: status.x, y: status.total });
});
// As above, we are building a domain size for the chart to use.
// Without this code the chart could render data outside of the field.
const checksDomain = upSeries.concat(downSeries).map(({ y }) => y);
const domainLimits = [Math.min(...checksDomain), Math.max(...checksDomain)];
const durationDomain = avgDurationSeries.concat(areaRttSeries);
const durationLimits = [0, Math.max(...durationDomain.map(({ y }) => y))];
const checkDomainLimits = [0, Math.max(...checksDomain)];
const durationDomain = avgDurationSeries.concat(areaDurationSeries);
const durationDomainLimits = [0, Math.max(...durationDomain.map(({ y }) => y))];
return (
<Fragment>
@ -144,21 +115,23 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
width={500}
height={200}
xType={EuiSeriesChartUtils.SCALE.TIME}
yDomain={durationLimits}
yDomain={durationDomainLimits}
crosshairValue={this.state.crosshairLocation}
onCrosshairUpdate={this.updateCrosshairLocation}
>
<EuiAreaSeries
color={secondary}
name={i18n.translate(
'xpack.uptime.monitorCharts.monitorDuration.series.durationRangeLabel',
{
defaultMessage: 'Duration range',
}
)}
data={areaRttSeries}
data={areaDurationSeries}
curve="curveBasis"
/>
<EuiLineSeries
color={primary}
name={i18n.translate(
'xpack.uptime.monitorCharts.monitorDuration.series.meanDurationLabel',
{
@ -188,7 +161,7 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
stackBy="y"
crosshairValue={this.state.crosshairLocation}
onCrosshairUpdate={this.updateCrosshairLocation}
yDomain={domainLimits}
yDomain={checkDomainLimits}
>
<EuiAreaSeries
name={i18n.translate(
@ -198,7 +171,8 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
}
)}
data={upSeries}
color="green"
curve="curveBasis"
color={primary}
/>
<EuiAreaSeries
name={i18n.translate(
@ -208,7 +182,7 @@ export class MonitorChartsQuery extends React.Component<Props, MonitorChartsStat
}
)}
data={downSeries}
color="red"
color={danger}
/>
</EuiSeriesChart>
</EuiPanel>

View file

@ -22,6 +22,7 @@ type Props = MonitorListProps & UptimeCommonProps;
export const MonitorListQuery = ({
autorefreshInterval,
autorefreshIsPaused,
colors: { primary, danger },
dateRangeStart,
dateRangeEnd,
filters,
@ -39,7 +40,14 @@ export const MonitorListQuery = ({
});
}
const monitors: LatestMonitor[] | undefined = get(data, 'monitorStatus.monitors', undefined);
return <MonitorList loading={loading} monitors={monitors || []} />;
return (
<MonitorList
dangerColor={danger}
loading={loading}
monitors={monitors || []}
primaryColor={primary}
/>
);
}}
</Query>
);

View file

@ -18,10 +18,11 @@ interface SnapshotProps {
type Props = SnapshotProps & UptimeCommonProps;
export const SnapshotQuery = ({
dateRangeStart,
dateRangeEnd,
autorefreshIsPaused,
autorefreshInterval,
colors: { primary, danger },
dateRangeStart,
dateRangeEnd,
filters,
}: Props) => (
<Query
@ -41,7 +42,7 @@ export const SnapshotQuery = ({
}
const { snapshot } = data;
return <Snapshot snapshot={snapshot} />;
return <Snapshot danger={danger} primary={primary} snapshot={snapshot} />;
}}
</Query>
);

View file

@ -9,7 +9,7 @@ import { unmountComponentAtNode } from 'react-dom';
import chrome from 'ui/chrome';
import { PLUGIN } from '../../../../common/constants';
import { UMBreadcrumb } from '../../../breadcrumbs';
import { UptimeCommonProps } from '../../../uptime_app';
import { UptimePersistedState } from '../../../uptime_app';
import { BootstrapUptimeApp, UMFrameworkAdapter } from '../../lib';
import { CreateGraphQLClient } from './framework_adapter_types';
@ -61,6 +61,7 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
? `${basePath}/${PLUGIN.ROUTER_BASE_NAME}`
: basePath + PLUGIN.ROUTER_BASE_NAME;
const persistedState = this.initializePersistedState();
const darkMode = config.get('theme:darkMode', false) || false;
const {
autorefreshIsPaused,
autorefreshInterval,
@ -69,6 +70,7 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
} = persistedState;
ReactDOM.render(
renderComponent({
darkMode,
isUsingK7Design: $scope.k7design,
updateBreadcrumbs: chrome.breadcrumbs.set,
kibanaBreadcrumbs,
@ -108,9 +110,9 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
});
};
private initializePersistedState = (): UptimeCommonProps => {
private initializePersistedState = (): UptimePersistedState => {
const uptimeConfigurationData = window.localStorage.getItem(PLUGIN.LOCAL_STORAGE_KEY);
const defaultState: UptimeCommonProps = {
const defaultState: UptimePersistedState = {
autorefreshIsPaused: this.defaultAutorefreshIsPaused,
autorefreshInterval: this.defaultAutorefreshInterval,
dateRangeStart: this.defaultDateRangeStart,
@ -141,7 +143,7 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter {
return defaultState;
};
private updatePersistedState = (state: UptimeCommonProps) => {
private updatePersistedState = (state: UptimePersistedState) => {
window.localStorage.setItem(PLUGIN.LOCAL_STORAGE_KEY, JSON.stringify(state));
};
}

View file

@ -21,6 +21,8 @@ import {
// @ts-ignore missing typings for EuiSuperDatePicker
EuiSuperDatePicker,
} from '@elastic/eui';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
@ -31,31 +33,47 @@ import { overviewBreadcrumb, UMBreadcrumb } from './breadcrumbs';
import { UMGraphQLClient, UMUpdateBreadcrumbs } from './lib/lib';
import { MonitorPage, OverviewPage } from './pages';
interface UptimeAppColors {
danger: string;
primary: string;
secondary: string;
}
// TODO: these props are global to this app, we should put them in a context
export interface UptimeCommonProps {
autorefreshIsPaused: boolean;
autorefreshInterval: number;
dateRangeStart: string;
dateRangeEnd: string;
colors: UptimeAppColors;
}
export interface UptimePersistedState {
autorefreshIsPaused: boolean;
autorefreshInterval: number;
dateRangeStart: string;
dateRangeEnd: string;
}
export interface UptimeAppProps {
isUsingK7Design: boolean;
updateBreadcrumbs: UMUpdateBreadcrumbs;
kibanaBreadcrumbs: UMBreadcrumb[];
routerBasename: string;
darkMode: boolean;
graphQLClient: UMGraphQLClient;
initialDateRangeStart: string;
initialDateRangeEnd: string;
initialAutorefreshInterval: number;
initialAutorefreshIsPaused: boolean;
persistState(state: UptimeCommonProps): void;
isUsingK7Design: boolean;
kibanaBreadcrumbs: UMBreadcrumb[];
routerBasename: string;
updateBreadcrumbs: UMUpdateBreadcrumbs;
persistState(state: UptimePersistedState): void;
}
interface UptimeAppState {
autorefreshIsPaused: boolean;
autorefreshInterval: number;
breadcrumbs: UMBreadcrumb[];
colors: UptimeAppColors;
dateRangeStart: string;
dateRangeEnd: string;
}
@ -77,13 +95,14 @@ class Application extends React.Component<UptimeAppProps, UptimeAppState> {
super(props);
const {
isUsingK7Design,
kibanaBreadcrumbs,
updateBreadcrumbs,
darkMode,
initialAutorefreshIsPaused: autorefreshIsPaused,
initialAutorefreshInterval: autorefreshInterval,
initialDateRangeStart: dateRangeStart,
initialDateRangeEnd: dateRangeEnd,
isUsingK7Design,
kibanaBreadcrumbs,
updateBreadcrumbs,
} = props;
let initialBreadcrumbs: UMBreadcrumb[];
@ -96,10 +115,26 @@ class Application extends React.Component<UptimeAppProps, UptimeAppState> {
initialBreadcrumbs = [overviewBreadcrumb];
}
let colors: UptimeAppColors;
if (darkMode) {
colors = {
primary: euiDarkVars.euiColorVis1,
secondary: euiDarkVars.euiColorVis0,
danger: euiDarkVars.euiColorVis9,
};
} else {
colors = {
primary: euiLightVars.euiColorVis1,
secondary: euiLightVars.euiColorVis0,
danger: euiLightVars.euiColorVis9,
};
}
this.state = {
autorefreshIsPaused,
autorefreshInterval,
breadcrumbs: initialBreadcrumbs,
colors,
dateRangeStart,
dateRangeEnd,
};