[Uptime] Add Donut chart (#47176) (#48037)

* Add donut chart.

* Merge donut chart with snapshot histogram panel.

* Update broken test snapshot.

* Undo removal of gray color from histogram chart.

* Use proper color values for donut chart and legend.

* Move donut chart to charts directory.

* Add snapshot tests for new chart components.

* Merge donut chart with snapshot histogram panel.

* Revert d3 version.

* Revert yarn.lock.

* Revert code to be compatible with older d3 version.

* Delete duplicated files.

* Clean up code.

* Improve const names.

* Update outdated snapshots.

* Fix broken type.

* Simplify function, add comment.

* Remove unused translations.

* Fix broken chart component.

* Implement designer feedback.

* Fix regression.

* Implement additional designer feedback.

* Fix issue that caused residual paths to remain on subsequent paints of donut chart.

* Update obsolete test snapshots.

* Revert yarn.lock.

* Rename repurposed translation.

* Delete obsolete translations.

* Update busted test snapshot.
This commit is contained in:
Justin Kambic 2019-10-14 22:09:13 -04:00 committed by GitHub
parent 9c11ab380b
commit bad9c8b762
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 635 additions and 310 deletions

View file

@ -1,62 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Snapshot component renders without errors 1`] = `
<Fragment>
<EuiPanel>
<EuiTitle
size="xs"
>
<h5>
<FormattedMessage
defaultMessage="Current status"
id="xpack.uptime.snapshot.endpointStatusTitle"
values={Object {}}
/>
</h5>
</EuiTitle>
<EuiFlexGroup
direction="column"
gutterSize="m"
>
<EuiFlexItem
grow={false}
>
<EuiSpacer
size="xs"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup
gutterSize="s"
justifyContent="spaceEvenly"
>
<EuiFlexItem>
<EuiStat
description="Up"
textAlign="center"
title={8}
titleColor="secondary"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
description="Down"
textAlign="center"
title={2}
titleColor="danger"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
description="Total"
textAlign="center"
title={10}
titleColor="subdued"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</Fragment>
<ChartWrapper
loading={false}
>
<EuiTitle
size="s"
>
<h2>
<FormattedMessage
defaultMessage="{down}/{total} monitors are down"
id="xpack.uptime.snapshot.downCountsMessage"
values={
Object {
"down": 2,
"total": 10,
}
}
/>
</h2>
</EuiTitle>
<EuiSpacer
size="xs"
/>
<DonutChart
down={2}
height={144}
up={8}
width={144}
/>
</ChartWrapper>
`;

View file

@ -0,0 +1,135 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DonutChart component passes correct props without errors for valid props 1`] = `
<EuiFlexGroup
alignItems="center"
responsive={false}
>
<EuiFlexItem
grow={false}
>
<svg
aria-label="Pie chart showing the current status. 32 of 127 monitors are down."
height={125}
width={125}
/>
</EuiFlexItem>
<EuiFlexItem>
<DonutChartLegend
down={32}
up={95}
/>
</EuiFlexItem>
</EuiFlexGroup>
`;
exports[`DonutChart component renders a donut chart 1`] = `
<div
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
<svg
aria-label="Pie chart showing the current status. 32 of 127 monitors are down."
height="125"
width="125"
/>
</div>
<div
class="euiFlexItem"
>
<div
class="sc-htpNat kGuflQ"
>
<div
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow"
>
<span
class="euiFlexItem euiFlexItem--flexGrowZero sc-bdVaJa fYBJge"
>
<div
class="euiHealth"
>
<div
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
<svg
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
style="fill:#bd271e"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
/>
</div>
</div>
</span>
<span
class="euiFlexItem euiFlexItem--flexGrowZero sc-bdVaJa fYBJge"
>
Down
</span>
<span
class="euiFlexItem sc-bwzfXH dkRrwr"
>
32
</span>
</div>
<div
class="euiSpacer euiSpacer--m"
/>
<div
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow"
>
<span
class="euiFlexItem euiFlexItem--flexGrowZero sc-bdVaJa fYBJge"
>
<div
class="euiHealth"
>
<div
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
<svg
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
style="fill:#d3dae6"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
/>
</div>
</div>
</span>
<span
class="euiFlexItem euiFlexItem--flexGrowZero sc-bdVaJa fYBJge"
>
Up
</span>
<span
class="euiFlexItem sc-bwzfXH dkRrwr"
>
95
</span>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DonutChartLegend applies valid props as expected 1`] = `
<styled.div>
<DonutChartLegendRow
color="#bd271e"
content={23}
message="Down"
/>
<EuiSpacer
size="m"
/>
<DonutChartLegendRow
color="#d3dae6"
content={45}
message="Up"
/>
</styled.div>
`;

View file

@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DonutChartLegendRow passes appropriate props 1`] = `
<EuiFlexGroup
gutterSize="l"
responsive={false}
>
<Styled(EuiFlexItem)
component="span"
grow={false}
>
<EuiHealth
color="green"
/>
</Styled(EuiFlexItem)>
<Styled(EuiFlexItem)
component="span"
grow={false}
>
Foo
</Styled(EuiFlexItem)>
<Styled(EuiFlexItem)
component="span"
>
23
</Styled(EuiFlexItem)>
</EuiFlexGroup>
`;

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { DonutChart } from '../donut_chart';
import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
describe('DonutChart component', () => {
it('passes correct props without errors for valid props', () => {
const props = {
down: 32,
up: 95,
height: 125,
width: 125,
};
const wrapper = shallowWithIntl(<DonutChart {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('renders a donut chart', () => {
const props = {
down: 32,
up: 95,
height: 125,
width: 125,
};
const wrapper = renderWithIntl(<DonutChart {...props} />);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { DonutChartLegend } from '../donut_chart_legend';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
describe('DonutChartLegend', () => {
it('applies valid props as expected', () => {
const wrapper = shallowWithIntl(<DonutChartLegend down={23} up={45} />);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { DonutChartLegendRow } from '../donut_chart_legend_row';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
describe('DonutChartLegendRow', () => {
it('passes appropriate props', () => {
const wrapper = shallowWithIntl(
<DonutChartLegendRow color="green" message="Foo" content={23} />
);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,87 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useContext, useEffect, useRef } from 'react';
import * as d3 from 'd3';
import { i18n } from '@kbn/i18n';
import { DonutChartLegend } from './donut_chart_legend';
import { UptimeSettingsContext } from '../../../contexts';
interface DonutChartProps {
down: number;
height: number;
up: number;
width: number;
}
export const DonutChart = ({ height, down, up, width }: DonutChartProps) => {
const chartElement = useRef<SVGSVGElement | null>(null);
const {
colors: { danger, gray },
} = useContext(UptimeSettingsContext);
useEffect(() => {
if (chartElement.current !== null) {
// we must remove any existing paths before painting
d3.selectAll('g').remove();
const svgElement = d3
.select(chartElement.current)
.append('g')
.attr('transform', `translate(${width / 2}, ${height / 2})`);
const color = d3.scale
.ordinal()
.domain(['up', 'down'])
.range([gray, danger]);
const pieGenerator = d3.layout
.pie()
.value(({ value }: any) => value)
// these start/end angles will reverse the direction of the pie,
// which matches our design
.startAngle(2 * Math.PI)
.endAngle(0);
svgElement
.selectAll('g')
.data(
// @ts-ignore pie generator expects param of type number[], but only works with
// output of d3.entries, which is like Array<{ key: string, value: number }>
pieGenerator(d3.entries({ up, down }))
)
.enter()
.append('path')
.attr(
'd',
// @ts-ignore attr does not expect a param of type Arc<Arc> but it behaves as desired
d3.svg
.arc()
.innerRadius(width * 0.28)
.outerRadius(Math.min(width, height) / 2 - 10)
)
.attr('fill', (d: any) => color(d.data.key));
}
}, [chartElement.current, up, down]);
return (
<EuiFlexGroup alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>
<svg
aria-label={i18n.translate('xpack.uptime.donutChart.ariaLabel', {
defaultMessage:
'Pie chart showing the current status. {down} of {total} monitors are down.',
values: { down, total: up + down },
})}
ref={chartElement}
width={width}
height={height}
/>
</EuiFlexItem>
<EuiFlexItem>
<DonutChartLegend down={down} up={up} />
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
import React, { useContext } from 'react';
import styled from 'styled-components';
import { DonutChartLegendRow } from './donut_chart_legend_row';
import { UptimeSettingsContext } from '../../../contexts';
const LegendContainer = styled.div`
max-width: 260px;
min-width: 100px;
@media (max-width: 767px) {
min-width: 0px;
max-width: 100px;
}
`;
interface Props {
down: number;
up: number;
}
export const DonutChartLegend = ({ down, up }: Props) => {
const {
colors: { gray, danger },
} = useContext(UptimeSettingsContext);
return (
<LegendContainer>
<DonutChartLegendRow
color={danger}
content={down}
message={i18n.translate('xpack.uptime.donutChart.legend.downRowLabel', {
defaultMessage: 'Down',
})}
/>
<EuiSpacer size="m" />
<DonutChartLegendRow
color={gray}
content={up}
message={i18n.translate('xpack.uptime.donutChart.legend.upRowLabel', {
defaultMessage: 'Up',
})}
/>
</LegendContainer>
);
};

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexGroup, EuiFlexItem, EuiHealth } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
const EuiFlexItemReducedMargin = styled(EuiFlexItem)`
&& {
margin-left: 0px;
margin-right: 0px;
}
`;
const EuiFlexItemAlignRight = styled(EuiFlexItem)`
text-align: right;
`;
interface Props {
color: string;
message: string;
content: string | number;
}
export const DonutChartLegendRow = ({ color, content, message }: Props) => (
<EuiFlexGroup gutterSize="l" responsive={false}>
<EuiFlexItemReducedMargin component="span" grow={false}>
<EuiHealth color={color} />
</EuiFlexItemReducedMargin>
<EuiFlexItemReducedMargin component="span" grow={false}>
{message}
</EuiFlexItemReducedMargin>
<EuiFlexItemAlignRight component="span">{content}</EuiFlexItemAlignRight>
</EuiFlexGroup>
);

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { DonutChart } from './donut_chart';
export { DurationChart } from './duration_chart';
export { MonitorBarSeries } from './monitor_bar_series';
export { SnapshotHistogram } from './snapshot_histogram';

View file

@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import moment from 'moment';
import styled from 'styled-components';
import { HistogramDataPoint } from '../../../../common/graphql/types';
import { getColorsMap } from './get_colors_map';
import { getChartDateLabel } from '../../../lib/helper';
@ -27,6 +28,17 @@ import { snapshotHistogramQuery } from '../../../queries/snapshot_histogram_quer
import { ChartWrapper } from './chart_wrapper';
import { UptimeSettingsContext } from '../../../contexts';
const SnapshotHistogramWrapper = styled.div`
margin-left: 120px;
@media (max-width: 950px) {
margin-left: 48px;
}
@media (max-width: 767px) {
margin-left: 12px;
margin-top: 40px;
}
`;
export interface SnapshotHistogramProps {
/**
* The date/time for the start of the timespan.
@ -96,99 +108,97 @@ export const SnapshotHistogramComponent = ({
</>
);
const { histogram } = data;
const downMonitorsName = i18n.translate('xpack.uptime.snapshotHistogram.downMonitorsId', {
const {
colors: { danger, gray },
} = useContext(UptimeSettingsContext);
const downMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.downMonitorsId', {
defaultMessage: 'Down Monitors',
});
const { colors } = useContext(UptimeSettingsContext);
const downSpecId = getSpecId(downMonitorsName);
const downSpecId = getSpecId(downMonitorsId);
const upMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.series.upLabel', {
defaultMessage: 'Up',
});
const upSpecId = getSpecId(upMonitorsId);
return (
<>
<EuiPanel paddingSize="m">
<EuiTitle size="xs">
<h5>
<FormattedMessage
id="xpack.uptime.snapshot.pingsOverTimeTitle"
defaultMessage="Pings over time"
/>
</h5>
</EuiTitle>
<ChartWrapper
height={height}
loading={loading}
aria-label={i18n.translate('xpack.uptime.snapshotHistogram.description', {
defaultMessage:
'Bar Chart showing uptime status over time from {startTime} to {endTime}.',
values: {
startTime: moment(new Date(absoluteStartDate).valueOf()).fromNow(),
endTime: moment(new Date(absoluteEndDate).valueOf()).fromNow(),
},
})}
>
<Chart>
<Settings
xDomain={{ min: absoluteStartDate, max: absoluteEndDate }}
showLegend={false}
/>
<Axis
id={getAxisId(
i18n.translate('xpack.uptime.snapshotHistogram.xAxisId', {
defaultMessage: 'Snapshot X Axis',
})
)}
position={Position.Bottom}
showOverlappingTicks={false}
tickFormat={timeFormatter(getChartDateLabel(absoluteStartDate, absoluteEndDate))}
/>
<Axis
id={getAxisId(
i18n.translate('xpack.uptime.snapshotHistogram.yAxisId', {
defaultMessage: 'Snapshot Y Axis',
})
)}
position="left"
title={i18n.translate('xpack.uptime.snapshotHistogram.yAxis.title', {
defaultMessage: 'Pings',
description:
'The label on the y-axis of a chart that displays the number of times Heartbeat has pinged a set of services/websites.',
})}
/>
<BarSeries
customSeriesColors={getColorsMap(colors.danger, downSpecId)}
data={histogram.map(({ x, downCount }) => [x, downCount || 0])}
id={downSpecId}
name={i18n.translate('xpack.uptime.snapshotHistogram.series.downLabel', {
defaultMessage: 'Down',
})}
stackAccessors={[0]}
timeZone="local"
xAccessor={0}
xScaleType="time"
yAccessors={[1]}
yScaleType="linear"
/>
<BarSeries
customSeriesColors={getColorsMap(colors.gray, upSpecId)}
data={histogram.map(({ x, upCount }) => [x, upCount || 0])}
id={upSpecId}
name={upMonitorsId}
stackAccessors={[0]}
timeZone="local"
xAccessor={0}
xScaleType="time"
yAccessors={[1]}
yScaleType="linear"
/>
</Chart>
</ChartWrapper>
</EuiPanel>
</>
<SnapshotHistogramWrapper>
<EuiTitle size="xs">
<h2>
<FormattedMessage
id="xpack.uptime.snapshot.pingsOverTimeTitle"
defaultMessage="Pings over time"
/>
</h2>
</EuiTitle>
<ChartWrapper
height={height}
loading={loading}
aria-label={i18n.translate('xpack.uptime.snapshotHistogram.description', {
defaultMessage:
'Bar Chart showing uptime status over time from {startTime} to {endTime}.',
values: {
startTime: moment(new Date(absoluteStartDate).valueOf()).fromNow(),
endTime: moment(new Date(absoluteEndDate).valueOf()).fromNow(),
},
})}
>
<Chart>
<Settings xDomain={{ min: absoluteStartDate, max: absoluteEndDate }} showLegend={false} />
<Axis
id={getAxisId(
i18n.translate('xpack.uptime.snapshotHistogram.xAxisId', {
defaultMessage: 'Snapshot X Axis',
})
)}
position={Position.Bottom}
showOverlappingTicks={false}
tickFormat={timeFormatter(getChartDateLabel(absoluteStartDate, absoluteEndDate))}
/>
<Axis
id={getAxisId(
i18n.translate('xpack.uptime.snapshotHistogram.yAxisId', {
defaultMessage: 'Snapshot Y Axis',
})
)}
position="left"
title={i18n.translate('xpack.uptime.snapshotHistogram.yAxis.title', {
defaultMessage: 'Pings',
description:
'The label on the y-axis of a chart that displays the number of times Heartbeat has pinged a set of services/websites.',
})}
/>
<BarSeries
customSeriesColors={getColorsMap(danger, downSpecId)}
data={histogram.map(({ x, downCount }) => [x, downCount || 0])}
id={downSpecId}
name={i18n.translate('xpack.uptime.snapshotHistogram.series.downLabel', {
defaultMessage: 'Down',
})}
stackAccessors={[0]}
timeZone="local"
xAccessor={0}
xScaleType="time"
yAccessors={[1]}
yScaleType="linear"
/>
<BarSeries
customSeriesColors={getColorsMap(gray, upSpecId)}
data={histogram.map(({ x, upCount }) => [x, upCount || 0])}
id={upSpecId}
name={upMonitorsId}
stackAccessors={[0]}
timeZone="local"
xAccessor={0}
xScaleType="time"
yAccessors={[1]}
yScaleType="linear"
/>
</Chart>
</ChartWrapper>
</SnapshotHistogramWrapper>
);
};

View file

@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { DonutChart } from './charts/donut_chart';
export { EmptyState } from './empty_state';
export { EmptyStatusBar } from './empty_status_bar';
export { FilterGroup } from './filter_group';
export { KueryBar } from './kuery_bar';
export { IntegrationLink } from './integration_link';
export { KueryBar } from './kuery_bar';
export { MonitorCharts } from './monitor_charts';
export { MonitorList } from './monitor_list';
export { MonitorPageLink } from './monitor_page_link';
@ -18,4 +19,4 @@ export { OverviewPageParsingErrorCallout } from './overview_page_parsing_error_c
export { PingList } from './ping_list';
export { Snapshot } from './snapshot';
export { SnapshotHistogram } from './charts';
export { SnapshotLoading } from './snapshot_loading';
export { StatusPanel } from './status_panel';

View file

@ -4,15 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat, EuiTitle } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiSpacer, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { get } from 'lodash';
import { DonutChart } from './charts';
import { Snapshot as SnapshotType } from '../../../common/graphql/types';
import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../higher_order';
import { snapshotQuery } from '../../queries';
import { SnapshotLoading } from './snapshot_loading';
import { ChartWrapper } from './charts/chart_wrapper';
const SNAPSHOT_CHART_WIDTH = 144;
const SNAPSHOT_CHART_HEIGHT = 144;
interface SnapshotQueryResult {
snapshot?: SnapshotType;
@ -23,74 +26,32 @@ interface SnapshotQueryResult {
* glean the status of their uptime environment.
* @param props the props required by the component
*/
export const SnapshotComponent = ({ data }: UptimeGraphQLQueryProps<SnapshotQueryResult>) =>
data && data.snapshot ? (
<React.Fragment>
<EuiPanel>
<EuiTitle size="xs">
<h5>
<FormattedMessage
id="xpack.uptime.snapshot.endpointStatusTitle"
defaultMessage="Current status"
/>
</h5>
</EuiTitle>
<EuiFlexGroup direction="column" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiSpacer size="xs" />
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceEvenly" gutterSize="s">
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.upDescription', {
defaultMessage: 'Up',
})}
textAlign="center"
title={data.snapshot.counts.up}
titleColor="secondary"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.downDescription', {
defaultMessage: 'Down',
})}
textAlign="center"
title={data.snapshot.counts.down}
titleColor="danger"
/>
</EuiFlexItem>
{data.snapshot.counts.mixed > 0 ? (
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.mixedDescription', {
defaultMessage: 'Mixed',
})}
textAlign="center"
title={data.snapshot.counts.mixed}
titleColor="subdued"
/>
</EuiFlexItem>
) : null}
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.totalDescription', {
defaultMessage: 'Total',
})}
textAlign="center"
title={data.snapshot.counts.total}
titleColor="subdued"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</React.Fragment>
) : (
<SnapshotLoading />
);
export const SnapshotComponent = ({
data,
loading,
}: UptimeGraphQLQueryProps<SnapshotQueryResult>) => (
<ChartWrapper loading={loading}>
<EuiTitle size="s">
<h2>
<FormattedMessage
id="xpack.uptime.snapshot.downCountsMessage"
defaultMessage="{down}/{total} monitors are down"
values={{
down: get<number>(data, 'snapshot.counts.down', 0),
total: get<number>(data, 'snapshot.counts.total', 0),
}}
/>
</h2>
</EuiTitle>
<EuiSpacer size="xs" />
<DonutChart
up={get<number>(data, 'snapshot.counts.up', 0)}
down={get<number>(data, 'snapshot.counts.down', 0)}
height={SNAPSHOT_CHART_HEIGHT}
width={SNAPSHOT_CHART_WIDTH}
/>
</ChartWrapper>
);
/**
* This component visualizes a KPI and histogram chart to help users quickly

View file

@ -1,69 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// @ts-ignore missing typings for EuiStat
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
export const SnapshotLoading = () => (
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={4}>
<Fragment>
<EuiTitle size="xs">
<h5>
<FormattedMessage
id="xpack.uptime.snapshot.endpointStatusLoadingTitle"
defaultMessage="Current status"
/>
</h5>
</EuiTitle>
<EuiPanel paddingSize="s" style={{ height: 170 }}>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceEvenly" gutterSize="s">
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.upDescription', {
defaultMessage: 'Up',
})}
textAlign="center"
title="-"
titleColor="secondary"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.downDescription', {
defaultMessage: 'Down',
})}
textAlign="center"
title="-"
titleColor="danger"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
description={i18n.translate('xpack.uptime.snapshot.stats.totalDescription', {
defaultMessage: 'Total',
})}
textAlign="center"
title="-"
titleColor="subdued"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</Fragment>
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import React from 'react';
import { SnapshotHistogram } from './charts';
import { Snapshot } from './snapshot';
import { UptimeAppColors } from '../../uptime_app';
interface StatusPanelProps {
absoluteDateRangeStart: number;
absoluteDateRangeEnd: number;
colors: UptimeAppColors;
sharedProps: { [key: string]: any };
}
export const StatusPanel = ({
absoluteDateRangeStart,
absoluteDateRangeEnd,
colors: { danger, success },
sharedProps,
}: StatusPanelProps) => (
<EuiPanel>
<EuiFlexGroup gutterSize="l">
<EuiFlexItem grow={2}>
<Snapshot variables={sharedProps} />
</EuiFlexItem>
<EuiFlexItem grow={10}>
<SnapshotHistogram
absoluteStartDate={absoluteDateRangeStart}
absoluteEndDate={absoluteDateRangeEnd}
variables={sharedProps}
height="160px"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);

View file

@ -17,8 +17,7 @@ import {
KueryBar,
MonitorList,
OverviewPageParsingErrorCallout,
Snapshot,
SnapshotHistogram,
StatusPanel,
} from '../components/functional';
import { UMUpdateBreadcrumbs } from '../lib/lib';
import { UptimeSettingsContext } from '../contexts';
@ -149,19 +148,12 @@ export const OverviewPage = ({
{error && <OverviewPageParsingErrorCallout error={error} />}
</EuiFlexGroup>
<EuiSpacer size="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={4}>
<Snapshot variables={sharedProps} />
</EuiFlexItem>
<EuiFlexItem grow={8}>
<SnapshotHistogram
absoluteStartDate={absoluteDateRangeStart}
absoluteEndDate={absoluteDateRangeEnd}
variables={sharedProps}
height="120px"
/>
</EuiFlexItem>
</EuiFlexGroup>
<StatusPanel
absoluteDateRangeStart={absoluteDateRangeStart}
absoluteDateRangeEnd={absoluteDateRangeEnd}
colors={colors}
sharedProps={sharedProps}
/>
<EuiSpacer size="s" />
<MonitorList
absoluteStartDate={absoluteDateRangeStart}

View file

@ -10820,13 +10820,8 @@
"xpack.uptime.pingList.statusOptions.downStatusOptionLabel": "ダウン",
"xpack.uptime.pingList.statusOptions.upStatusOptionLabel": "アップ",
"xpack.uptime.pluginDescription": "アップタイム監視",
"xpack.uptime.snapshot.endpointStatusLoadingTitle": "現在のステータス",
"xpack.uptime.snapshot.endpointStatusTitle": "現在のステータス",
"xpack.uptime.snapshot.noDataDescription": "申し訳ございませんが、ヒストグラムに利用可能なデータがありません",
"xpack.uptime.snapshot.noDataTitle": "ヒストグラムデータがありません",
"xpack.uptime.snapshot.stats.downDescription": "ダウン",
"xpack.uptime.snapshot.stats.totalDescription": "合計",
"xpack.uptime.snapshot.stats.upDescription": "アップ",
"xpack.uptime.snapshotHistogram.series.downLabel": "ダウン",
"xpack.uptime.snapshotHistogram.series.upLabel": "アップ",
"xpack.uptime.uptimeFeatureCatalogueTitle": "起動時間",

View file

@ -10985,13 +10985,8 @@
"xpack.uptime.pingList.statusOptions.downStatusOptionLabel": "关闭",
"xpack.uptime.pingList.statusOptions.upStatusOptionLabel": "运行",
"xpack.uptime.pluginDescription": "运行时间监测",
"xpack.uptime.snapshot.endpointStatusLoadingTitle": "当前状态",
"xpack.uptime.snapshot.endpointStatusTitle": "当前状态",
"xpack.uptime.snapshot.noDataDescription": "抱歉,没有可用于该直方图的数据",
"xpack.uptime.snapshot.noDataTitle": "没有可用的直方图数据",
"xpack.uptime.snapshot.stats.downDescription": "关闭",
"xpack.uptime.snapshot.stats.totalDescription": "合计",
"xpack.uptime.snapshot.stats.upDescription": "运行",
"xpack.uptime.snapshotHistogram.series.downLabel": "关闭",
"xpack.uptime.snapshotHistogram.series.upLabel": "运行",
"xpack.uptime.uptimeFeatureCatalogueTitle": "运行时间",