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] Integrates the color service #93749

Merged
merged 22 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from 15 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
7 changes: 6 additions & 1 deletion src/plugins/vis_type_timeseries/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type PanelSchema = TypeOf<typeof panel>;
export type VisPayload = TypeOf<typeof visPayloadSchema>;
export type FieldObject = TypeOf<typeof fieldObject>;

interface PanelData {
export interface PanelData {
id: string;
label: string;
data: Array<[number, number]>;
Expand Down Expand Up @@ -57,3 +57,8 @@ export interface SanitizedFieldType {
type: string;
label?: string;
}

export enum PALETTES {
GRADIENT = 'gradient',
RAINBOW = 'rainbow',
}
4 changes: 4 additions & 0 deletions src/plugins/vis_type_timeseries/common/vis_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ export const seriesItems = schema.object({
series_interval: stringOptionalNullable,
series_drop_last_bucket: numberIntegerOptional,
split_color_mode: stringOptionalNullable,
palette: schema.object({
type: stringRequired,
name: stringRequired,
}),
split_filters: schema.maybe(schema.arrayOf(splitFiltersItems)),
split_mode: stringRequired,
stacked: stringRequired,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 { mountWithIntl } from '@kbn/test/jest';
import { ReactWrapper } from 'enzyme';
import { PalettePicker, PalettePickerProps } from './palette_picker';
import { chartPluginMock } from '../../../../charts/public/mocks';
import { EuiColorPalettePicker } from '@elastic/eui';
import { PALETTES } from '../../../common/types';

describe('PalettePicker', function () {
let props: PalettePickerProps;
let component: ReactWrapper<PalettePickerProps>;

beforeAll(() => {
props = {
palettes: chartPluginMock.createPaletteRegistry(),
activePalette: {
type: 'palette',
name: 'kibana_palette',
},
setPalette: jest.fn(),
color: '#68BC00',
};
});

it('renders the EuiPalettePicker', () => {
component = mountWithIntl(<PalettePicker {...props} />);
expect(component.find(EuiColorPalettePicker).length).toBe(1);
});

it('renders the default palette if not activePalette is given', function () {
const { activePalette, ...newProps } = props;
component = mountWithIntl(<PalettePicker {...newProps} />);
const palettePicker = component.find(EuiColorPalettePicker);
expect(palettePicker.props().valueOfSelected).toBe('default');
});

it('renders the activePalette palette if given', function () {
component = mountWithIntl(<PalettePicker {...props} />);
const palettePicker = component.find(EuiColorPalettePicker);
expect(palettePicker.props().valueOfSelected).toBe('kibana_palette');
});

it('renders two additional palettes, rainbow and gradient', function () {
component = mountWithIntl(<PalettePicker {...props} />);
const palettePicker = component.find(EuiColorPalettePicker);
expect(palettePicker.props().palettes).toEqual(
expect.arrayContaining([
expect.objectContaining({
value: PALETTES.RAINBOW,
}),
expect.objectContaining({
value: PALETTES.GRADIENT,
}),
])
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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 { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
import { EuiColorPalettePicker } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { rainbowColors } from '../lib/rainbow_colors';
import { computeGradientFinalColor } from '../lib/compute_gradient_final_color';
import { PALETTES } from '../../../common/types';

export interface PalettePickerProps {
activePalette?: PaletteOutput;
palettes: PaletteRegistry;
setPalette: (value: PaletteOutput) => void;
color: string;
}

export function PalettePicker({ activePalette, palettes, setPalette, color }: PalettePickerProps) {
const finalGradientColor = computeGradientFinalColor(color);

return (
<EuiColorPalettePicker
fullWidth
data-test-subj="visEditorPalettePicker"
compressed
palettes={[
...palettes
.getAll()
.filter(({ internal }) => !internal)
.map(({ id, title, getColors }) => {
return {
value: id,
title,
type: 'fixed' as const,
palette: getColors(10),
};
}),
{
value: PALETTES.GRADIENT,
title: i18n.translate('visTypeTimeseries.timeSeries.gradientLabel', {
defaultMessage: 'Gradient',
}),
type: 'fixed',
palette: palettes
.get('custom')
.getColors(10, { colors: [color, finalGradientColor], gradient: true }),
},
{
value: PALETTES.RAINBOW,
title: i18n.translate('visTypeTimeseries.timeSeries.rainbowLabel', {
defaultMessage: 'Rainbow',
}),
type: 'fixed',
palette: palettes
.get('custom')
.getColors(10, { colors: rainbowColors.slice(0, 10), gradient: false }),
},
]}
onChange={(newPalette) => {
if (newPalette === PALETTES.RAINBOW) {
setPalette({
type: 'palette',
name: PALETTES.RAINBOW,
params: {
colors: rainbowColors,
gradient: false,
},
});
} else if (newPalette === PALETTES.GRADIENT) {
setPalette({
type: 'palette',
name: PALETTES.GRADIENT,
params: {
colors: [color, finalGradientColor],
gradient: true,
},
});
} else {
setPalette({
type: 'palette',
name: newPalette,
});
}
}}
valueOfSelected={activePalette?.name || 'default'}
selectionDisplay={'palette'}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface TimeseriesVisualizationProps {
model: TimeseriesVisParams;
visData: TimeseriesVisData;
uiState: PersistedState;
syncColors: boolean;
}

function TimeseriesVisualization({
Expand All @@ -34,6 +35,7 @@ function TimeseriesVisualization({
handlers,
uiState,
getConfig,
syncColors,
}: TimeseriesVisualizationProps) {
const onBrush = useCallback(
(gte: string, lte: string) => {
Expand Down Expand Up @@ -91,6 +93,7 @@ function TimeseriesVisualization({
uiState={uiState}
onBrush={onBrush}
onUiState={handleUiState}
syncColors={syncColors}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ export interface TimeseriesVisProps {
uiState: PersistedState;
visData: TimeseriesVisData;
getConfig: IUiSettingsClient['get'];
syncColors: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import PropTypes from 'prop-types';
import React from 'react';
import React, { useState, useEffect } from 'react';
import { DataFormatPicker } from '../../data_format_picker';
import { createSelectHandler } from '../../lib/create_select_handler';
import { YesNo } from '../../yes_no';
Expand All @@ -28,7 +28,8 @@ import {
} from '@elastic/eui';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import { SeriesConfigQueryBarWithIgnoreGlobalFilter } from '../../series_config_query_bar_with_ignore_global_filter';

import { PalettePicker } from '../../palette_picker';
import { getChartsSetup } from '../../../../services';
import { isPercentDisabled } from '../../lib/stacked';
import { STACKED_OPTIONS } from '../../../visualizations/constants/chart';

Expand All @@ -41,7 +42,6 @@ export const TimeseriesConfig = injectI18n(function (props) {
point_size: '',
value_template: '{{value}}',
offset_time: '',
split_color_mode: 'kibana',
axis_min: '',
axis_max: '',
stacked: STACKED_OPTIONS.NONE,
Expand Down Expand Up @@ -124,33 +124,23 @@ export const TimeseriesConfig = injectI18n(function (props) {
const selectedChartTypeOption = chartTypeOptions.find((option) => {
return model.chart_type === option.value;
});
const { palettes } = getChartsSetup();
const [palettesRegistry, setPalettesRegistry] = useState(null);

const splitColorOptions = [
{
label: intl.formatMessage({
id: 'visTypeTimeseries.timeSeries.defaultPaletteLabel',
defaultMessage: 'Default palette',
}),
value: 'kibana',
},
{
label: intl.formatMessage({
id: 'visTypeTimeseries.timeSeries.rainbowLabel',
defaultMessage: 'Rainbow',
}),
value: 'rainbow',
},
{
label: intl.formatMessage({
id: 'visTypeTimeseries.timeSeries.gradientLabel',
defaultMessage: 'Gradient',
}),
value: 'gradient',
},
];
const selectedSplitColorOption = splitColorOptions.find((option) => {
return model.split_color_mode === option.value;
});
useEffect(() => {
const fetchPalettes = async () => {
const palettesService = await palettes.getPalettes();
setPalettesRegistry(palettesService);
};
fetchPalettes();
}, [palettes]);

const handlePaletteChange = (val) => {
props.onChange({
split_color_mode: null,
Copy link
Contributor Author

@stratoula stratoula Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to be backwards compatible I haven't removed the split_color_mode. Otherwise, I had to write a migration script to get its value and use it as the palette name. I preferred to skip the migration script.

palette: val,
});
};

let type;

Expand Down Expand Up @@ -342,6 +332,14 @@ export const TimeseriesConfig = injectI18n(function (props) {
? props.model.series_index_pattern
: props.indexPatternForQuery;

const initialPalette = {
...model.palette,
name:
model.split_color_mode === 'kibana'
? 'kibana_palette'
: model.split_color_mode || model.palette.name,
};

return (
<div className="tvbAggRow">
<EuiFlexGroup gutterSize="s">
Expand Down Expand Up @@ -420,25 +418,26 @@ export const TimeseriesConfig = injectI18n(function (props) {
<EuiSpacer size="s" />
<YesNo value={model.hide_in_legend} name="hide_in_legend" onChange={props.onChange} />
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('splitColor')}
label={
<FormattedMessage
id="visTypeTimeseries.timeSeries.splitColorThemeLabel"
defaultMessage="Split color theme"
{palettesRegistry && (
<EuiFlexItem grow={true}>
<EuiFormRow
id={htmlId('splitColor')}
label={
<FormattedMessage
id="visTypeTimeseries.timeSeries.splitColorThemeLabel"
defaultMessage="Split color theme"
/>
}
>
<PalettePicker
palettes={palettesRegistry}
activePalette={initialPalette}
setPalette={handlePaletteChange}
color={model.color}
/>
}
>
<EuiComboBox
isClearable={false}
options={splitColorOptions}
selectedOptions={selectedSplitColorOption ? [selectedSplitColorOption] : []}
onChange={handleSelectChange('split_color_mode')}
singleSelection={{ asPlainText: true }}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFormRow>
</EuiFlexItem>
)}
</EuiFlexGroup>

<EuiHorizontalRule margin="s" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class TimeseriesVisualization extends Component {
};

render() {
const { model, visData, onBrush } = this.props;
const { model, visData, onBrush, syncColors } = this.props;
const series = get(visData, `${model.id}.series`, []);
const interval = getInterval(visData, model);
const yAxisIdGenerator = htmlIdGenerator('yaxis');
Expand Down Expand Up @@ -182,6 +182,9 @@ class TimeseriesVisualization extends Component {
seriesDataRow.groupId = groupId;
seriesDataRow.yScaleType = yScaleType;
seriesDataRow.hideInLegend = Boolean(seriesGroup.hide_in_legend);
seriesDataRow.palette = seriesGroup.palette;
seriesDataRow.baseColor = seriesGroup.color;
seriesDataRow.isSplitByTerms = seriesGroup.split_mode === 'terms';
});

if (isCustomDomain) {
Expand Down Expand Up @@ -223,6 +226,7 @@ class TimeseriesVisualization extends Component {
xAxisLabel={getAxisLabelString(interval)}
xAxisFormatter={this.xAxisFormatter(interval)}
annotations={this.prepareAnnotations()}
syncColors={syncColors}
/>
</div>
);
Expand Down
Loading