Skip to content

Commit

Permalink
[Infra UI] Add UI to customize Metrics Explorer chart style (#41022)
Browse files Browse the repository at this point in the history
* Add UI to customize Metrics Explorer chart style

* Re-order chart options form

* Adding chart options to TSVB link

* Rename line series to series chart

* Fixing chart context menu tests

* Adding test for calculate domain

* Ensure caclulateDomain returns numbers

* fixing typo
  • Loading branch information
simianhacker authored Jul 22, 2019
1 parent 75694c4 commit bbd8980
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/t
import {
MetricsExplorerOptions,
MetricsExplorerTimeOptions,
MetricsExplorerYAxisMode,
MetricsExplorerChartOptions,
} from '../../containers/metrics_explorer/use_metrics_explorer_options';
import euiStyled from '../../../../../common/eui_styled_components';
import { createFormatterForMetric } from './helpers/create_formatter_for_metric';
import { MetricLineSeries } from './line_series';
import { MetricExplorerSeriesChart } from './series_chart';
import { MetricsExplorerChartContextMenu } from './chart_context_menu';
import { SourceQuery } from '../../graphql/types';
import { MetricsExplorerEmptyChart } from './empty_chart';
import { MetricsExplorerNoMetrics } from './no_metrics';
import { getChartTheme } from './helpers/get_chart_theme';
import { calculateDomain } from './helpers/calculate_domain';

interface Props {
intl: InjectedIntl;
Expand All @@ -33,6 +36,7 @@ interface Props {
width?: number | string;
height?: number | string;
options: MetricsExplorerOptions;
chartOptions: MetricsExplorerChartOptions;
series: MetricsExplorerSeries;
source: SourceQuery.Query['source']['configuration'] | undefined;
timeRange: MetricsExplorerTimeOptions;
Expand All @@ -45,6 +49,7 @@ export const MetricsExplorerChart = injectUICapabilities(
({
source,
options,
chartOptions,
series,
title,
onFilter,
Expand All @@ -66,6 +71,11 @@ export const MetricsExplorerChart = injectUICapabilities(
[series.rows]
);
const yAxisFormater = useCallback(createFormatterForMetric(first(metrics)), [options]);
const dataDomain = calculateDomain(series, metrics, chartOptions.stack);
const domain =
chartOptions.yAxisMode === MetricsExplorerYAxisMode.fromZero
? { ...dataDomain, min: 0 }
: dataDomain;
return (
<div style={{ padding: 24 }}>
{options.groupBy ? (
Expand All @@ -80,6 +90,7 @@ export const MetricsExplorerChart = injectUICapabilities(
<MetricsExplorerChartContextMenu
timeRange={timeRange}
options={options}
chartOptions={chartOptions}
series={series}
onFilter={onFilter}
source={source}
Expand All @@ -93,6 +104,7 @@ export const MetricsExplorerChart = injectUICapabilities(
<EuiFlexItem grow={false}>
<MetricsExplorerChartContextMenu
options={options}
chartOptions={chartOptions}
series={series}
source={source}
timeRange={timeRange}
Expand All @@ -105,7 +117,14 @@ export const MetricsExplorerChart = injectUICapabilities(
{series.rows.length > 0 ? (
<Chart>
{metrics.map((metric, id) => (
<MetricLineSeries key={id} metric={metric} id={id} series={series} />
<MetricExplorerSeriesChart
type={chartOptions.type}
key={id}
metric={metric}
id={id}
series={series}
stack={chartOptions.stack}
/>
))}
<Axis
id={getAxisId('timestamp')}
Expand All @@ -117,6 +136,7 @@ export const MetricsExplorerChart = injectUICapabilities(
id={getAxisId('values')}
position={Position.Left}
tickFormat={yAxisFormater}
domain={domain}
/>
<Settings onBrushEnd={handleTimeChange} theme={getChartTheme()} />
</Chart>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React from 'react';
import { MetricsExplorerChartContextMenu, createNodeDetailLink } from './chart_context_menu';
import { mountWithIntl } from '../../utils/enzyme_helpers';
import { options, source, timeRange } from '../../utils/fixtures/metrics_explorer';
import { options, source, timeRange, chartOptions } from '../../utils/fixtures/metrics_explorer';
import { UICapabilities } from 'ui/capabilities';
import { InfraNodeType } from '../../graphql/types';
import DateMath from '@elastic/datemath';
Expand Down Expand Up @@ -37,6 +37,7 @@ describe('MetricsExplorerChartContextMenu', () => {
options={options}
onFilter={onFilter}
uiCapabilities={uiCapabilities}
chartOptions={chartOptions}
/>
);

Expand All @@ -57,6 +58,7 @@ describe('MetricsExplorerChartContextMenu', () => {
options={customOptions}
onFilter={onFilter}
uiCapabilities={uiCapabilities}
chartOptions={chartOptions}
/>
);
component.find('button').simulate('click');
Expand All @@ -71,6 +73,7 @@ describe('MetricsExplorerChartContextMenu', () => {
series={series}
options={options}
uiCapabilities={uiCapabilities}
chartOptions={chartOptions}
/>
);

Expand All @@ -89,6 +92,7 @@ describe('MetricsExplorerChartContextMenu', () => {
options={customOptions}
onFilter={onFilter}
uiCapabilities={uiCapabilities}
chartOptions={chartOptions}
/>
);

Expand All @@ -105,6 +109,7 @@ describe('MetricsExplorerChartContextMenu', () => {
series={series}
options={customOptions}
uiCapabilities={uiCapabilities}
chartOptions={chartOptions}
/>
);

Expand All @@ -125,6 +130,7 @@ describe('MetricsExplorerChartContextMenu', () => {
options={options}
onFilter={onFilter}
uiCapabilities={customUICapabilities}
chartOptions={chartOptions}
/>
);

Expand All @@ -144,6 +150,7 @@ describe('MetricsExplorerChartContextMenu', () => {
options={customOptions}
onFilter={onFilter}
uiCapabilities={customUICapabilities}
chartOptions={chartOptions}
/>
);
expect(component.find('button').length).toBe(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/t
import {
MetricsExplorerOptions,
MetricsExplorerTimeOptions,
MetricsExplorerChartOptions,
} from '../../containers/metrics_explorer/use_metrics_explorer_options';
import { createTSVBLink } from './helpers/create_tsvb_link';
import { InfraNodeType } from '../../graphql/types';
Expand All @@ -31,6 +32,7 @@ interface Props {
source?: SourceConfiguration;
timeRange: MetricsExplorerTimeOptions;
uiCapabilities: UICapabilities;
chartOptions: MetricsExplorerChartOptions;
}

const fieldToNodeType = (source: SourceConfiguration, field: string): InfraNodeType | undefined => {
Expand Down Expand Up @@ -66,7 +68,7 @@ export const createNodeDetailLink = (
};

export const MetricsExplorerChartContextMenu = injectI18n(
({ intl, onFilter, options, series, source, timeRange, uiCapabilities }: Props) => {
({ intl, onFilter, options, series, source, timeRange, uiCapabilities, chartOptions }: Props) => {
const [isPopoverOpen, setPopoverState] = useState(false);
const supportFiltering = options.groupBy != null && onFilter != null;
const handleFilter = useCallback(() => {
Expand All @@ -78,7 +80,7 @@ export const MetricsExplorerChartContextMenu = injectI18n(
setPopoverState(false);
}, [supportFiltering, options.groupBy, series.id, onFilter]);

const tsvbUrl = createTSVBLink(source, options, series, timeRange);
const tsvbUrl = createTSVBLink(source, options, series, timeRange, chartOptions);

// Only display the "Add Filter" option if it's supported
const filterByItem = supportFiltering
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* 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 React, { useState, useCallback } from 'react';
import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
EuiRadioGroup,
EuiButtonEmpty,
EuiPopover,
EuiForm,
EuiFormRow,
EuiSwitch,
} from '@elastic/eui';
import {
MetricsExplorerChartOptions as ChartOptions,
MetricsExplorerYAxisMode,
MetricsExplorerChartType,
} from '../../containers/metrics_explorer/use_metrics_explorer_options';

interface Props {
chartOptions: ChartOptions;
onChange: (options: ChartOptions) => void;
intl: InjectedIntl;
}

export const MetricsExplorerChartOptions = injectI18n(({ chartOptions, onChange, intl }: Props) => {
const [isPopoverOpen, setPopoverState] = useState<boolean>(false);

const handleClosePopover = useCallback(() => {
setPopoverState(false);
}, []);

const handleOpenPopover = useCallback(() => {
setPopoverState(true);
}, []);

const button = (
<EuiButtonEmpty iconSide="left" iconType="eye" onClick={handleOpenPopover}>
<FormattedMessage
id="xpack.infra.metricsExplorer.customizeChartOptions"
defaultMessage="Customize"
/>
</EuiButtonEmpty>
);

const yAxisRadios = [
{
id: MetricsExplorerYAxisMode.auto,
label: intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.autoLabel',
defaultMessage: 'Automatic (Min to Max)',
}),
},
{
id: MetricsExplorerYAxisMode.fromZero,
label: intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.fromZeroLabel',
defaultMessage: 'From Zero (0 to Max)',
}),
},
];

const typeRadios = [
{
id: MetricsExplorerChartType.line,
label: intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.lineLabel',
defaultMessage: 'Line',
}),
},
{
id: MetricsExplorerChartType.area,
label: intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.areaLabel',
defaultMessage: 'Area',
}),
},
];

const handleYAxisChange = useCallback(
(id: string) => {
onChange({
...chartOptions,
yAxisMode: id as MetricsExplorerYAxisMode,
});
},
[chartOptions, onChange]
);

const handleTypeChange = useCallback(
(id: string) => {
onChange({
...chartOptions,
type: id as MetricsExplorerChartType,
});
},
[chartOptions, onChange]
);

const handleStackChange = useCallback(
e => {
onChange({
...chartOptions,
stack: e.target.checked,
});
},
[chartOptions, onChange]
);

return (
<EuiPopover
id="MetricExplorerChartOptionsPopover"
button={button}
isOpen={isPopoverOpen}
closePopover={handleClosePopover}
>
<EuiForm>
<EuiFormRow
compressed
label={intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.typeLabel',
defaultMessage: 'Chart Style',
})}
>
<EuiRadioGroup
options={typeRadios}
idSelected={chartOptions.type}
onChange={handleTypeChange}
/>
</EuiFormRow>
<EuiFormRow
compressed
label={intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.stackLabel',
defaultMessage: 'Stack Series',
})}
>
<EuiSwitch
label={intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.stackSwitchLabel',
defaultMessage: 'Stack',
})}
checked={chartOptions.stack}
onChange={handleStackChange}
/>
</EuiFormRow>
<EuiFormRow
compressed
label={intl.formatMessage({
id: 'xpack.infra.metricsExplorer.chartOptions.yAxisDomainLabel',
defaultMessage: 'Y Axis Domain',
})}
>
<EuiRadioGroup
options={yAxisRadios}
idSelected={chartOptions.yAxisMode}
onChange={handleYAxisChange}
/>
</EuiFormRow>
</EuiForm>
</EuiPopover>
);
});
Loading

0 comments on commit bbd8980

Please sign in to comment.