Threshold line on bar/line/area charts (#42632) (#43762)

* first steps towards threshold line (histogram)

* threshold line added for all point_series charts

* added settings for threshold line

* last fixes

* fixed typo

* default values for thresholdLineOptions

* resolving conflicts

* threshold line not displayed when out of the canvas

* linting

* added color picker for threshold line

* fixed assigning of a static color and i18 select options

* changing default color and lintings

* Fix remaining TS issues
This commit is contained in:
Tim Roes 2019-08-22 16:06:01 +02:00 committed by GitHub
parent c597b6cf8d
commit 76ed46a4f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 203 additions and 3 deletions

View file

@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { PointSeriesOptions } from './editors/point_series';
import { getLegendPositions, LegendPositions } from './utils/legend_positions';
import { palettes } from '@elastic/eui/lib/services';
export default function PointSeriesVisType(Private) {
const VisFactory = Private(VisFactoryProvider);
@ -97,7 +98,14 @@ export default function PointSeriesVisType(Private) {
legendPosition: LegendPositions.RIGHT,
times: [],
addTimeMarker: false,
labels: {},
thresholdLine: {
show: false,
value: 10,
width: 1,
style: 'full',
color: palettes.euiPaletteColorBlind.colors[9]
},
labels: {}
},
},
editorConfig: {

View file

@ -24,6 +24,7 @@ interface NumberInputOptionProps<ParamName extends string> {
label?: React.ReactNode;
max?: number;
min?: number;
step?: string | number;
paramName: ParamName;
value?: number | '';
setValue: (paramName: ParamName, value: number | '') => void;
@ -34,6 +35,7 @@ function NumberInputOption<ParamName extends string>({
max,
min,
paramName,
step,
value = '',
setValue,
}: NumberInputOptionProps<ParamName>) {
@ -41,6 +43,7 @@ function NumberInputOption<ParamName extends string>({
<EuiFormRow label={label} fullWidth compressed>
<EuiFieldNumber
fullWidth
step={step}
max={max}
min={min}
value={value}

View file

@ -24,7 +24,7 @@ interface SelectOptionProps<ParamName extends string, ValidParamValues extends s
id?: string;
label: string;
labelAppend?: React.ReactNode;
options: Array<{ value: ValidParamValues; text: string }>;
options: ReadonlyArray<{ readonly value: ValidParamValues; readonly text: string }>;
paramName: ParamName;
value?: ValidParamValues;
setValue: (paramName: ParamName, value: ValidParamValues) => void;

View file

@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { EuiPanel, EuiTitle, EuiSpacer, EuiColorPicker, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@ -26,9 +26,31 @@ import { SwitchOption } from '../controls/switch';
import { GridOptions } from '../controls/point_series/grid_options';
import { BasicOptions } from '../controls/basic_options';
import { BasicVislibParams } from '../types';
import { NumberInputOption } from '../controls/number_input';
import { SelectOption } from '../controls/select';
function PointSeriesOptions(props: VisOptionsProps<BasicVislibParams>) {
const { stateParams, setValue, vis } = props;
const options = [
{
value: 'full',
text: i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.style.full', {
defaultMessage: 'Full',
}),
},
{
value: 'dashed',
text: i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.style.dashed', {
defaultMessage: 'Dashed',
}),
},
{
value: 'dot-dashed',
text: i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.style.dotdashed', {
defaultMessage: 'Dot-dashed',
}),
},
] as const;
return (
<>
@ -82,6 +104,90 @@ function PointSeriesOptions(props: VisOptionsProps<BasicVislibParams>) {
<EuiSpacer size="s" />
<GridOptions {...props} />
<EuiSpacer size="s" />
<EuiPanel paddingSize="s">
<EuiTitle size="xs">
<h2>
<FormattedMessage
id="kbnVislibVisTypes.editors.pointSeries.thresholdLineSettings"
defaultMessage="Threshold Line"
/>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<SwitchOption
label={i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.show', {
defaultMessage: 'Show threshold line',
})}
paramName="show"
value={stateParams.thresholdLine.show}
setValue={(paramName, value) =>
setValue('thresholdLine', { ...stateParams.thresholdLine, [paramName]: value })
}
/>
{stateParams.thresholdLine.show && (
<>
<NumberInputOption
label={i18n.translate(
'kbnVislibVisTypes.editors.pointSeries.thresholdLine.valueLabel',
{
defaultMessage: 'Threshold value',
}
)}
paramName="value"
value={stateParams.thresholdLine.value}
setValue={(paramName, value) =>
setValue('thresholdLine', { ...stateParams.thresholdLine, [paramName]: value || 0 })
}
/>
<NumberInputOption
label={i18n.translate(
'kbnVislibVisTypes.editors.pointSeries.thresholdLine.widthLabel',
{
defaultMessage: 'Line width',
}
)}
paramName="width"
min={1}
step={1}
value={stateParams.thresholdLine.width}
setValue={(paramName, value) =>
setValue('thresholdLine', { ...stateParams.thresholdLine, [paramName]: value || 1 })
}
/>
<SelectOption
label={i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.style', {
defaultMessage: 'Line style',
})}
options={options}
paramName="style"
value={stateParams.thresholdLine.style}
setValue={(paramName, value) =>
setValue('thresholdLine', { ...stateParams.thresholdLine, [paramName]: value })
}
/>
<EuiFormRow
label={i18n.translate('kbnVislibVisTypes.editors.pointSeries.thresholdLine.color', {
defaultMessage: 'Line color',
})}
>
<EuiColorPicker
color={stateParams.thresholdLine.color}
onChange={value => {
setValue('thresholdLine', { ...stateParams.thresholdLine, color: value });
}}
/>
</EuiFormRow>
</>
)}
</EuiPanel>
</>
);
}

View file

@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { PointSeriesOptions } from './editors/point_series';
import { getLegendPositions, LegendPositions } from './utils/legend_positions';
import { palettes } from '@elastic/eui/lib/services';
export default function PointSeriesVisType(Private) {
const VisFactory = Private(VisFactoryProvider);
@ -101,6 +102,13 @@ export default function PointSeriesVisType(Private) {
addTimeMarker: false,
labels: {
show: false,
},
thresholdLine: {
show: false,
value: 10,
width: 1,
style: 'full',
color: palettes.euiPaletteColorBlind.colors[9]
}
},
},

View file

@ -22,6 +22,8 @@ import { i18n } from '@kbn/i18n';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { PointSeriesOptions } from './editors/point_series';
import { getLegendPositions, LegendPositions } from './utils/legend_positions';
import { palettes } from '@elastic/eui/lib/services';
export default function PointSeriesVisType(Private) {
const VisFactory = Private(VisFactoryProvider);
@ -98,6 +100,13 @@ export default function PointSeriesVisType(Private) {
times: [],
addTimeMarker: false,
labels: {},
thresholdLine: {
show: false,
value: 10,
width: 1,
style: 'full',
color: palettes.euiPaletteColorBlind.colors[9]
}
},
},
editorConfig: {

View file

@ -30,6 +30,13 @@ interface Labels {
show: boolean;
truncate: number;
}
interface ThresholdLine {
show: boolean;
value: number;
width: number;
style: 'full' | 'dashed' | 'dot-dashed';
color: string;
}
export interface ValueAxis {
id: string;
labels: Labels;
@ -46,6 +53,7 @@ export interface BasicVislibParams extends CommonVislibParams {
addTimeMarker: boolean;
orderBucketsBySum?: boolean;
labels: Labels;
thresholdLine: ThresholdLine;
valueAxes: ValueAxis[];
grid: {
categoryLines: boolean;

View file

@ -18,6 +18,15 @@
*/
import _ from 'lodash';
import { palettes } from '@elastic/eui/lib/services';
const thresholdLineDefaults = {
show: false,
value: 10,
width: 1,
style: 'full',
color: palettes.euiPaletteColorBlind.colors[9],
};
export class PointSeries {
constructor(handler, seriesEl, seriesData, seriesConfig) {
@ -26,6 +35,7 @@ export class PointSeries {
this.chartEl = seriesEl;
this.chartData = seriesData;
this.seriesConfig = seriesConfig;
this.thresholdLineOptions = _.defaults(handler.visConfig.get('thresholdLine', {}), thresholdLineDefaults);
}
getGroupedCount() {
@ -80,4 +90,41 @@ export class PointSeries {
const click = events.addClickEvent();
return element.call(click);
}
addThresholdLine(svgElem) {
const chartData = this.chartData;
const isHorizontal = this.getCategoryAxis().axisConfig.isHorizontal();
const valueAxisDomain = this.getValueAxis().axisScale.getDomain(chartData.values.length);
const yScale = this.getValueAxis().getScale();
const svgParentWidth = svgElem[0][0].attributes.width.value;
const svgParentHeight = svgElem[0][0].attributes.height.value;
const thresholdLineWidth = this.thresholdLineOptions.width;
let thresholdLineStyle = '0';
if (this.thresholdLineOptions.style === 'dashed') {
thresholdLineStyle = '10,5';
} else if (this.thresholdLineOptions.style === 'dot-dashed') {
thresholdLineStyle = '20,5,5,5';
}
const thresholdValue = this.thresholdLineOptions.value;
const lineColor = this.thresholdLineOptions.color;
function y(y) {
return yScale(y);
}
if (valueAxisDomain && valueAxisDomain[0] <= thresholdValue && valueAxisDomain[1] >= thresholdValue) {
svgElem
.append('line')
.attr('x1', isHorizontal ? 0 : y(thresholdValue))
.attr('y1', isHorizontal ? y(thresholdValue) : 0)
.attr('x2', isHorizontal ? svgParentWidth : y(thresholdValue))
.attr('y2', isHorizontal ? y(thresholdValue) : svgParentHeight)
.attr('stroke-width', thresholdLineWidth)
.attr('stroke-dasharray', thresholdLineStyle)
.attr('stroke', lineColor);
}
}
}

View file

@ -252,6 +252,9 @@ export class AreaChart extends PointSeries {
const circles = self.addCircles(svg, self.chartData);
self.addCircleEvents(circles);
if (self.thresholdLineOptions.show) {
self.addThresholdLine(self.chartEl);
}
self.events.emit('rendered', {
chart: self.chartData
});

View file

@ -367,6 +367,10 @@ export class ColumnChart extends PointSeries {
const bars = self.addBars(svg, self.chartData);
self.addCircleEvents(bars);
if (self.thresholdLineOptions.show) {
self.addThresholdLine(self.chartEl);
}
self.events.emit('rendered', {
chart: self.chartData
});

View file

@ -224,6 +224,10 @@ export class LineChart extends PointSeries {
const circles = self.addCircles(svg, self.chartData);
self.addCircleEvents(circles);
if (self.thresholdLineOptions.show) {
self.addThresholdLine(self.chartEl);
}
self.events.emit('rendered', {
chart: self.chartData
});