Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TSVB] Adds a color picker in percentiles and percentiles rank aggs #107390

Merged
merged 14 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/plugins/vis_type_timeseries/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export const ROUTES = {
FIELDS: '/api/metrics/fields',
};
export const USE_KIBANA_INDEXES_KEY = 'use_kibana_indexes';
export const TSVB_DEFAULT_COLOR = '#68BC00';
2 changes: 2 additions & 0 deletions src/plugins/vis_type_timeseries/common/types/panel_model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface Percentile {
shade?: number | string;
value?: number | string;
percentile?: string;
color?: string;
}

export interface Metric {
Expand Down Expand Up @@ -52,6 +53,7 @@ export interface Metric {
type: string;
value?: string;
values?: string[];
colors?: string[];
size?: string | number;
agg_with?: string;
order?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const runTest = (aggType, name, test, additionalProps = {}) => {
...additionalProps,
};
const series = { ...SERIES, metrics: [metric] };
const panel = { ...PANEL, series };
const panel = PANEL;

it(name, () => {
const wrapper = mountWithIntl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,13 @@ export function PercentileAgg(props) {
/>
}
>
<Percentiles onChange={handleChange} name="percentiles" model={model} panel={panel} />
<Percentiles
onChange={handleChange}
name="percentiles"
model={model}
panel={panel}
seriesId={series.id}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { shallowWithIntl } from '@kbn/test/jest';
import { MultiValueRow } from './multi_value_row';
import { ColorPicker } from '../../color_picker';

describe('MultiValueRow', () => {
const model = {
id: 95,
value: '95',
color: '#00028',
};
const props = {
model,
enableColorPicker: true,
onChange: jest.fn(),
onDelete: jest.fn(),
onAdd: jest.fn(),
disableAdd: false,
disableDelete: false,
};

const wrapper = shallowWithIntl(<MultiValueRow {...props} />);

it('displays a color picker if the enableColorPicker prop is true', () => {
expect(wrapper.find(ColorPicker).length).toEqual(1);
});

it('not displays a color picker if the enableColorPicker prop is false', () => {
const newWrapper = shallowWithIntl(<MultiValueRow {...props} enableColorPicker={false} />);
expect(newWrapper.find(ColorPicker).length).toEqual(0);
});

it('sets the picker color to the model color', () => {
expect(wrapper.find(ColorPicker).prop('value')).toEqual('#00028');
});

it('should have called the onChange function on color change', () => {
wrapper.find(ColorPicker).simulate('change');
expect(props.onChange).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ import React, { ChangeEvent } from 'react';
import { get } from 'lodash';

import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import { TSVB_DEFAULT_COLOR } from '../../../../../common/constants';

import { AddDeleteButtons } from '../../add_delete_buttons';
import { ColorPicker, ColorProps } from '../../color_picker';

interface MultiValueRowProps {
model: {
id: number;
value: string;
color: string;
};
disableAdd: boolean;
disableDelete: boolean;
onChange: ({ value, id }: { id: number; value: string }) => void;
enableColorPicker: boolean;
onChange: ({ value, id, color }: { id: number; value: string; color: string }) => void;
onDelete: (model: { id: number; value: string }) => void;
onAdd: () => void;
}
Expand All @@ -32,16 +36,33 @@ export const MultiValueRow = ({
onAdd,
disableAdd,
disableDelete,
enableColorPicker,
}: MultiValueRowProps) => {
const onFieldNumberChange = (event: ChangeEvent<HTMLInputElement>) =>
onChange({
...model,
value: get(event, 'target.value'),
});

const onColorPickerChange = (props: ColorProps) =>
onChange({
...model,
color: props?.color || TSVB_DEFAULT_COLOR,
});

return (
<EuiPanel paddingSize="s" className="tvbAggRow__multiValueRow">
<EuiFlexGroup alignItems="center" gutterSize="s">
{enableColorPicker && (
<EuiFlexItem grow={false}>
<ColorPicker
disableTrash={true}
onChange={onColorPickerChange}
value={model.color}
name="color"
/>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiFieldNumber
value={model.value === '' ? '' : Number(model.value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import { AggRow } from '../agg_row';
import { PercentileRankValues } from './percentile_rank_values';

import { KBN_FIELD_TYPES } from '../../../../../../data/public';
import type { Metric, Panel, SanitizedFieldType } from '../../../../../common/types';
import type { Metric, Panel, SanitizedFieldType, Series } from '../../../../../common/types';
import { TSVB_DEFAULT_COLOR } from '../../../../../common/constants';

import { DragHandleProps } from '../../../../types';
import { PercentileHdr } from '../percentile_hdr';

Expand All @@ -40,6 +42,7 @@ interface PercentileRankAggProps {
model: Metric;
panel: Panel;
siblings: Metric[];
series: Series;
dragHandleProps: DragHandleProps;
onAdd(): void;
onChange(): void;
Expand All @@ -48,19 +51,25 @@ interface PercentileRankAggProps {

export const PercentileRankAgg = (props: PercentileRankAggProps) => {
const { panel, fields, indexPattern } = props;
const defaults = { values: [''] };
const defaults = { values: [''], colors: [TSVB_DEFAULT_COLOR] };
const model = { ...defaults, ...props.model };

const htmlId = htmlIdGenerator();
const isTablePanel = panel.type === 'table';
const handleChange = createChangeHandler(props.onChange, model);
const handleSelectChange = createSelectHandler(handleChange);
const handleNumberChange = createNumberHandler(handleChange);
const percentileRankSeries =
panel.series.find((s) => s.id === props.series.id) || panel.series[0];
// If the series is grouped by, then these colors are not respected, no need to display the color picker */
const isGroupedBy = panel.series.length > 0 && percentileRankSeries.split_mode !== 'everything';
const enableColorPicker = !isGroupedBy && !['table', 'metric', 'markdown'].includes(panel.type);

const handlePercentileRankValuesChange = (values: Metric['values']) => {
const handlePercentileRankValuesChange = (values: Metric['values'], colors: Metric['colors']) => {
handleChange({
...model,
values,
colors,
});
};
return (
Expand Down Expand Up @@ -119,8 +128,10 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => {
disableAdd={isTablePanel}
disableDelete={isTablePanel}
showOnlyLastRow={isTablePanel}
model={model.values!}
values={model.values!}
colors={model.colors!}
onChange={handlePercentileRankValuesChange}
enableColorPicker={enableColorPicker}
/>
</EuiFormRow>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,43 @@ import React from 'react';
import { last } from 'lodash';

import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { TSVB_DEFAULT_COLOR } from '../../../../../common/constants';
import { MultiValueRow } from './multi_value_row';

interface PercentileRankValuesProps {
model: Array<string | null>;
values: string[];
colors: string[];
disableDelete: boolean;
disableAdd: boolean;
showOnlyLastRow: boolean;
onChange: (values: any[]) => void;
enableColorPicker: boolean;
onChange: (values: string[], colors: string[]) => void;
}

export const PercentileRankValues = (props: PercentileRankValuesProps) => {
const model = props.model || [];
const { onChange, disableAdd, disableDelete, showOnlyLastRow } = props;
const values = props.values || [];
const colors = props.colors || [];
const { onChange, disableAdd, disableDelete, showOnlyLastRow, enableColorPicker } = props;

const onChangeValue = ({ value, id }: { value: string; id: number }) => {
model[id] = value;
const onChangeValue = ({ value, id, color }: { value: string; id: number; color: string }) => {
values[id] = value;
colors[id] = color;

onChange(model);
onChange(values, colors);
};
const onDeleteValue = ({ id }: { id: number }) =>
onChange(model.filter((item, currentIndex) => id !== currentIndex));
const onAddValue = () => onChange([...model, '']);
onChange(
values.filter((item, currentIndex) => id !== currentIndex),
colors.filter((item, currentIndex) => id !== currentIndex)
);
const onAddValue = () => onChange([...values, ''], [...colors, TSVB_DEFAULT_COLOR]);

const renderRow = ({
rowModel,
disableDeleteRow,
disableAddRow,
}: {
rowModel: { id: number; value: string };
rowModel: { id: number; value: string; color: string };
disableDeleteRow: boolean;
disableAddRow: boolean;
}) => (
Expand All @@ -50,6 +58,7 @@ export const PercentileRankValues = (props: PercentileRankValuesProps) => {
disableDelete={disableDeleteRow}
disableAdd={disableAddRow}
model={rowModel}
enableColorPicker={enableColorPicker}
/>
</EuiFlexItem>
);
Expand All @@ -59,19 +68,21 @@ export const PercentileRankValues = (props: PercentileRankValuesProps) => {
{showOnlyLastRow &&
renderRow({
rowModel: {
id: model.length - 1,
value: last(model) || '',
id: values.length - 1,
value: last(values) || '',
color: last(colors) || TSVB_DEFAULT_COLOR,
},
disableAddRow: true,
disableDeleteRow: true,
})}

{!showOnlyLastRow &&
model.map((value, id, array) =>
values.map((value, id, array) =>
renderRow({
rowModel: {
id,
value: value || '',
color: colors[id] || TSVB_DEFAULT_COLOR,
},
disableAddRow: disableAdd,
disableDeleteRow: disableDelete || array.length < 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import { TSVB_DEFAULT_COLOR } from '../../../../common/constants';
import { collectionActions } from '../lib/collection_actions';
import { AddDeleteButtons } from '../add_delete_buttons';
import uuid from 'uuid';
Expand All @@ -23,10 +24,11 @@ import {
EuiFlexGrid,
EuiPanel,
} from '@elastic/eui';
import { ColorPicker } from '../color_picker';
import { FormattedMessage } from '@kbn/i18n/react';

export const newPercentile = (opts) => {
return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts);
return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2, color: TSVB_DEFAULT_COLOR }, opts);
};

export class Percentiles extends Component {
Expand All @@ -39,11 +41,20 @@ export class Percentiles extends Component {
};
}

handleColorChange(item) {
return (val) => {
const handleChange = collectionActions.handleChange.bind(null, this.props);
handleChange(_.assign({}, item, val));
};
}

renderRow = (row, i, items) => {
const defaults = { value: '', percentile: '', shade: '' };
const defaults = { value: '', percentile: '', shade: '', color: TSVB_DEFAULT_COLOR };
const model = { ...defaults, ...row };
const { panel } = this.props;
const { panel, seriesId } = this.props;
const flexItemStyle = { minWidth: 100 };
const percentileSeries = panel.series.find((s) => s.id === seriesId) || panel.series[0];
const isGroupedBy = panel.series.length > 0 && percentileSeries.split_mode !== 'everything';

const percentileFieldNumber = (
<EuiFlexItem style={flexItemStyle}>
Expand Down Expand Up @@ -106,7 +117,19 @@ export class Percentiles extends Component {
<EuiPanel>
<EuiFlexGroup key={model.id} alignItems="center">
<EuiFlexItem>
<EuiFlexGrid columns={2}>
<EuiFlexGrid columns={3}>
{/* If the series is grouped by, then these colors are not respected,
no need to display the color picker */}
{!isGroupedBy && !['table', 'metric', 'markdown'].includes(panel.type) && (
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
<ColorPicker
disableTrash={true}
onChange={this.handleColorChange(model)}
value={model.color}
name="color"
/>
</EuiFlexItem>
)}
{percentileFieldNumber}
<EuiFlexItem style={flexItemStyle}>
<EuiFormRow
Expand Down Expand Up @@ -206,4 +229,5 @@ Percentiles.propTypes = {
model: PropTypes.object,
panel: PropTypes.object,
onChange: PropTypes.func,
seriesId: PropTypes.string,
};
Loading