Skip to content

Commit

Permalink
Threshold line on bar/line/area charts (elastic#42632)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
FilipRy authored and timroes committed Aug 22, 2019
1 parent c597b6c commit 09a8ea7
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 3 deletions.
10 changes: 9 additions & 1 deletion src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,13 +35,15 @@ function NumberInputOption<ParamName extends string>({
max,
min,
paramName,
step,
value = '',
setValue,
}: NumberInputOptionProps<ParamName>) {
return (
<EuiFormRow label={label} fullWidth compressed>
<EuiFieldNumber
fullWidth
step={step}
max={max}
min={min}
value={value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 (
<>
Expand Down Expand Up @@ -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>
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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]
}
},
},
Expand Down
9 changes: 9 additions & 0 deletions src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: {
Expand Down
8 changes: 8 additions & 0 deletions src/legacy/core_plugins/kbn_vislib_vis_types/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -46,6 +53,7 @@ export interface BasicVislibParams extends CommonVislibParams {
addTimeMarker: boolean;
orderBucketsBySum?: boolean;
labels: Labels;
thresholdLine: ThresholdLine;
valueAxes: ValueAxis[];
grid: {
categoryLines: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down

0 comments on commit 09a8ea7

Please sign in to comment.