Skip to content

Commit

Permalink
[Canvas] HeatMap. (#120239)
Browse files Browse the repository at this point in the history
* Added defaults to expressions.

* Fixed error on number passed as a name.

* One more fix.

* Added Heatmap element to Canvas.

* Added support of nested expressions.

* Added support of the adding models/views as arguments of the expressions.

* Added support of the name from parent configuration.

* Added support of removing nested models.

* Added heatmap legend description)

* Replaced help and displayName of legend.

* Added heatmap_grid.

* Fixed label.

* Added context of nested expressions support.

* Fixed bugs with updating of elements.

* Added color picker.

* Make color compressed

* Added usable inputs with good user experience.

* Reduced number of props, passing to the arg..

* Percentage and range args with debounce/

* Removed not used args from heatmap_grid

* fixed arg name.

* Fixed storybooks.

* Fixed one more story.

* Fixed unused args from lens.

* Added comments to the recursive function.

* Added docs to the transformNestedFunctionsToUIConfig

* Removed not used translations.

* Fixed tests.

* Added rest of arguments.

* Fixed args defaults generating.

* Fixed tests of lens.

* Changed '@kbn/interpreter/common' to '@kbn/interpreter'.

* Changed names of setArgumentAtIndex and addArgumentValueAtIndex and changed comments.

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
Kuznietsov and kibanamachine authored Jan 12, 2022
1 parent 0493f00 commit dd9d846
Show file tree
Hide file tree
Showing 51 changed files with 1,258 additions and 376 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import {
EXPRESSION_HEATMAP_LEGEND_NAME,
} from '../constants';

const convertToVisDimension = (columns: DatatableColumn[], accessor: string) => {
const convertToVisDimension = (
columns: DatatableColumn[],
accessor: string
): ExpressionValueVisDimension | undefined => {
const column = columns.find((c) => c.id === accessor);
if (!column) return;
return {
Expand All @@ -27,7 +30,7 @@ const convertToVisDimension = (columns: DatatableColumn[], accessor: string) =>
params: { ...column.meta.params?.params },
},
type: 'vis_dimension',
} as ExpressionValueVisDimension;
};
};

const prepareHeatmapLogTable = (
Expand Down Expand Up @@ -70,12 +73,14 @@ export const heatmapFunction = (): HeatmapExpressionFunctionDefinition => ({
help: i18n.translate('expressionHeatmap.function.legendConfig.help', {
defaultMessage: 'Configure the chart legend.',
}),
default: `{${EXPRESSION_HEATMAP_LEGEND_NAME}}`,
},
gridConfig: {
types: [EXPRESSION_HEATMAP_GRID_NAME],
help: i18n.translate('expressionHeatmap.function.gridConfig.help', {
defaultMessage: 'Configure the heatmap layout.',
}),
default: `{${EXPRESSION_HEATMAP_GRID_NAME}}`,
},
showTooltip: {
types: ['boolean'],
Expand Down Expand Up @@ -118,6 +123,7 @@ export const heatmapFunction = (): HeatmapExpressionFunctionDefinition => ({
help: i18n.translate('expressionHeatmap.function.args.valueAccessorHelpText', {
defaultMessage: 'The id of the value column or the corresponding dimension',
}),
required: true,
},
// not supported yet, small multiples accessor
splitRowAccessor: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const heatmapGridConfig: ExpressionFunctionDefinition<
name: EXPRESSION_HEATMAP_GRID_NAME,
aliases: [],
type: EXPRESSION_HEATMAP_GRID_NAME,
help: `Configure the heatmap layout `,
help: `Configure the heatmap layout`,
inputTypes: ['null'],
args: {
// grid
Expand All @@ -38,20 +38,6 @@ export const heatmapGridConfig: ExpressionFunctionDefinition<
}),
required: false,
},
cellHeight: {
types: ['number'],
help: i18n.translate('expressionHeatmap.function.args.grid.cellHeight.help', {
defaultMessage: 'Specifies the grid cell height',
}),
required: false,
},
cellWidth: {
types: ['number'],
help: i18n.translate('expressionHeatmap.function.args.grid.cellWidth.help', {
defaultMessage: 'Specifies the grid cell width',
}),
required: false,
},
// cells
isCellLabelVisible: {
types: ['boolean'],
Expand All @@ -66,20 +52,6 @@ export const heatmapGridConfig: ExpressionFunctionDefinition<
defaultMessage: 'Specifies whether or not the Y-axis labels are visible.',
}),
},
yAxisLabelWidth: {
types: ['number'],
help: i18n.translate('expressionHeatmap.function.args.grid.yAxisLabelWidth.help', {
defaultMessage: 'Specifies the width of the Y-axis labels.',
}),
required: false,
},
yAxisLabelColor: {
types: ['string'],
help: i18n.translate('expressionHeatmap.function.args.grid.yAxisLabelColor.help', {
defaultMessage: 'Specifies the color of the Y-axis labels.',
}),
required: false,
},
// X-axis
isXAxisLabelVisible: {
types: ['boolean'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,10 @@ export interface HeatmapGridConfig {
// grid
strokeWidth?: number;
strokeColor?: string;
cellHeight?: number;
cellWidth?: number;
// cells
isCellLabelVisible: boolean;
// Y-axis
isYAxisLabelVisible: boolean;
yAxisLabelWidth?: number;
yAxisLabelColor?: string;
// X-axis
isXAxisLabelVisible: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
const percentageNumber = (Math.abs(value - min) / (max - min)) * 100;
value = parseInt(percentageNumber.toString(), 10) / 100;
}
return metricFormatter.convert(value);
return `${metricFormatter.convert(value) ?? ''}`;
};

const { colors, ranges } = computeColorRanges(
Expand Down Expand Up @@ -415,7 +415,8 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
name: yAxisColumn?.name ?? '',
...(yAxisColumn
? {
formatter: (v: number | string) => formatFactory(yAxisColumn.meta.params).convert(v),
formatter: (v: number | string) =>
`${formatFactory(yAxisColumn.meta.params).convert(v) ?? ''}`,
}
: {}),
},
Expand All @@ -424,7 +425,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
// eui color subdued
textColor: chartTheme.axes?.tickLabel?.fill ?? `#6a717d`,
padding: xAxisColumn?.name ? 8 : 0,
formatter: (v: number | string) => xValuesFormatter.convert(v),
formatter: (v: number | string) => `${xValuesFormatter.convert(v) ?? ''}`,
name: xAxisColumn?.name ?? '',
},
brushMask: {
Expand Down
20 changes: 20 additions & 0 deletions x-pack/plugins/canvas/canvas_plugin_src/elements/heatmap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ElementFactory } from '../../../types';

export const heatmap: ElementFactory = () => ({
name: 'heatmap',
displayName: 'Heatmap',
type: 'chart',
help: 'Heatmap visualization',
icon: 'heatmap',
expression: `filters
| demodata
| head 10
| heatmap xAccessor={visdimension "age"} yAccessor={visdimension "project"} valueAccessor={visdimension "cost"}
| render`,
});
2 changes: 2 additions & 0 deletions x-pack/plugins/canvas/canvas_plugin_src/elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { verticalProgressBar } from './vertical_progress_bar';
import { verticalProgressPill } from './vertical_progress_pill';
import { tagCloud } from './tag_cloud';
import { metricVis } from './metric_vis';
import { heatmap } from './heatmap';

import { SetupInitializer } from '../plugin';
import { ElementFactory } from '../../types';
Expand Down Expand Up @@ -63,6 +64,7 @@ const elementSpecs = [
verticalProgressBar,
verticalProgressPill,
tagCloud,
heatmap,
];

const initializeElementFactories = [metricElementInitializer];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { FC } from 'react';
import PropTypes from 'prop-types';
import {
EuiColorPicker,
EuiFlexGroup,
EuiFlexItem,
EuiSetColorMethod,
useColorPickerState,
} from '@elastic/eui';
import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component';
import { withDebounceArg } from '../../../../public/components/with_debounce_arg';
import { ArgumentStrings } from '../../../../i18n';

const { Color: strings } = ArgumentStrings;

interface Props {
onValueChange: (value: string) => void;
argValue: string;
}

const ColorPicker: FC<Props> = ({ onValueChange, argValue }) => {
const [color, setColor, errors] = useColorPickerState(argValue);

const pickColor: EuiSetColorMethod = (value, meta) => {
setColor(value, meta);
onValueChange(value);
};

return (
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiColorPicker compressed onChange={pickColor} color={color} isInvalid={!!errors} />
</EuiFlexItem>
</EuiFlexGroup>
);
};

ColorPicker.propTypes = {
argValue: PropTypes.any.isRequired,
onValueChange: PropTypes.func.isRequired,
};

export const colorPicker = () => ({
name: 'color_picker',
displayName: strings.getDisplayName(),
help: strings.getHelp(),
simpleTemplate: templateFromReactComponent(withDebounceArg(ColorPicker)),
default: '"#000"',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { colorPicker } from './color_picker';
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const getMathValue = (argValue, columns) => {
// TODO: Garbage, we could make a much nicer math form that can handle way more.
const DatacolumnArgInput = ({
onValueChange,
columns,
resolved: { columns },
argValue,
renderError,
argId,
Expand Down Expand Up @@ -123,7 +123,9 @@ const DatacolumnArgInput = ({
};

DatacolumnArgInput.propTypes = {
columns: PropTypes.array.isRequired,
resolved: PropTypes.shape({
columns: PropTypes.array.isRequired,
}).isRequired,
onValueChange: PropTypes.func.isRequired,
typeInstance: PropTypes.object.isRequired,
renderError: PropTypes.func.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { textarea } from './textarea';
// @ts-expect-error untyped local
import { toggle } from './toggle';
import { visdimension } from './vis_dimension';
import { colorPicker } from './color_picker';

import { SetupInitializer } from '../../plugin';

Expand All @@ -51,6 +52,7 @@ export const args = [
textarea,
toggle,
visdimension,
colorPicker,
];

export const initializers = [dateFormatInitializer, numberFormatInitializer];
Expand Down
10 changes: 7 additions & 3 deletions x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { EuiFieldNumber, EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { templateFromReactComponent } from '../../../public/lib/template_from_react_component';
import { withDebounceArg } from '../../../public/components/with_debounce_arg';
import { ArgumentStrings } from '../../../i18n';

const { Number: strings } = ArgumentStrings;
Expand All @@ -28,8 +29,11 @@ const NumberArgInput = ({ argId, argValue, typeInstance, onValueChange }) => {

const onChange = useCallback(
(ev) => {
const onChangeFn = confirm ? setValue : onValueChange;
onChangeFn(ev.target.value);
const { value } = ev.target;
setValue(value);
if (!confirm) {
onValueChange(value);
}
},
[confirm, onValueChange]
);
Expand Down Expand Up @@ -62,6 +66,6 @@ export const number = () => ({
name: 'number',
displayName: strings.getDisplayName(),
help: strings.getHelp(),
simpleTemplate: templateFromReactComponent(NumberArgInput),
simpleTemplate: templateFromReactComponent(withDebounceArg(NumberArgInput)),
default: '0',
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
* 2.0.
*/

import React from 'react';
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { EuiRange } from '@elastic/eui';
import { withDebounceArg } from '../../../public/components/with_debounce_arg';
import { templateFromReactComponent } from '../../../public/lib/template_from_react_component';
import { ArgumentStrings } from '../../../i18n';

const { Percentage: strings } = ArgumentStrings;

const PercentageArgInput = ({ onValueChange, argValue }) => {
const handleChange = (ev) => {
return onValueChange(ev.target.value / 100);
};
const [value, setValue] = useState(argValue);

const handleChange = useCallback(
(ev) => {
const { value } = ev.target;
const numberVal = Number(value) / 100;
setValue(numberVal);
onValueChange(numberVal);
},
[onValueChange]
);

return (
<EuiRange
Expand All @@ -25,7 +34,7 @@ const PercentageArgInput = ({ onValueChange, argValue }) => {
max={100}
showLabels
showInput
value={argValue * 100}
value={value * 100}
onChange={handleChange}
/>
);
Expand All @@ -41,5 +50,5 @@ export const percentage = () => ({
name: 'percentage',
displayName: strings.getDisplayName(),
help: strings.getHelp(),
simpleTemplate: templateFromReactComponent(PercentageArgInput),
simpleTemplate: templateFromReactComponent(withDebounceArg(PercentageArgInput, 50)),
});
Loading

0 comments on commit dd9d846

Please sign in to comment.