Skip to content

Commit

Permalink
✨ Enable value labels for Heatmap
Browse files Browse the repository at this point in the history
  • Loading branch information
dej611 committed Oct 19, 2021
1 parent f129dac commit 5f21476
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 213 deletions.
3 changes: 1 addition & 2 deletions x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import type { LayerArgs } from './layer_config';
import type { LegendConfigResult } from './legend_config';
import type { TickLabelsConfigResult } from './tick_labels_config';
import type { LabelsOrientationConfigResult } from './labels_orientation_config';

export type ValueLabelConfig = 'hide' | 'inside' | 'outside';
import type { ValueLabelConfig } from '../../types';

export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X';

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/lens/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ export interface CustomPaletteParams {
export type RequiredPaletteParamTypes = Required<CustomPaletteParams>;

export type LayerType = 'data' | 'referenceLine';

// Shared by XY Chart and Heatmap as for now
export type ValueLabelConfig = 'hide' | 'inside' | 'outside';
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = ({
maxHeight: 'fill',
label: {
visible: args.gridConfig.isCellLabelVisible ?? false,
minFontSize: 8,
maxFontSize: 18,
useGlobalMinFontSize: true, // override the min if there's a different directive upstream
},
border: {
strokeWidth: 0,
Expand Down
105 changes: 21 additions & 84 deletions x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
* 2.0.
*/

import React, { FC, memo } from 'react';
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
import React, { memo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Position } from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import type { VisualizationToolbarProps } from '../types';
import { LegendSettingsPopover, ToolbarPopover } from '../shared_components';
import { LegendSettingsPopover, ToolbarPopover, ValueLabelsSettings } from '../shared_components';
import type { HeatmapVisualizationState } from './types';

const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [
Expand Down Expand Up @@ -40,15 +40,24 @@ export const HeatmapToolbar = memo(
<EuiFlexGroup gutterSize="m" justifyContent="spaceBetween" responsive={false}>
<EuiFlexItem>
<EuiFlexGroup gutterSize="none" responsive={false}>
<VisualOptionsPopover
valueLabels={state.gridConfig.isCellLabelVisible}
onValueLabelChange={(value) =>
setState({
...state,
gridConfig: { ...state.gridConfig, isCellLabelVisible: value },
})
}
/>
<ToolbarPopover
title={i18n.translate('xpack.lens.shared.curveLabel', {
defaultMessage: 'Visual options',
})}
type="visualOptions"
groupPosition="left"
buttonDataTestSubj="lnsVisualOptionsButton"
>
<ValueLabelsSettings
valueLabels={state?.gridConfig.isCellLabelVisible ? 'inside' : 'hide'}
onValueLabelChange={(newMode) => {
setState({
...state,
gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'inside' },
});
}}
/>
</ToolbarPopover>
<LegendSettingsPopover
groupPosition={'right'}
legendOptions={legendOptions}
Expand Down Expand Up @@ -96,75 +105,3 @@ export const HeatmapToolbar = memo(
);
}
);

const valueLabelsOptions: Array<{
id: string;
value: boolean;
label: string;
'data-test-subj': string;
}> = [
{
id: `value_labels_hide`,
value: false,
label: i18n.translate('xpack.lens.heatmapChart.valueLabelsVisibility.auto', {
defaultMessage: 'Hide',
}),
'data-test-subj': 'lnsXY_valueLabels_hide',
},
{
id: `value_labels_show`,
value: true,
label: i18n.translate('xpack.lens.heatmapChart.valueLabelsVisibility.inside', {
defaultMessage: 'Show',
}),
'data-test-subj': 'lnsXY_valueLabels_inside',
},
];

interface VisualOptionsProps {
valueLabels?: boolean;
onValueLabelChange: (newMode: boolean) => void;
}

const VisualOptionsPopover: FC<VisualOptionsProps> = ({
onValueLabelChange,
valueLabels = 'hide',
}) => {
return (
<ToolbarPopover
title={i18n.translate('xpack.lens.shared.visualOptions', {
defaultMessage: 'Visual options',
})}
type="visualOptions"
groupPosition="left"
buttonDataTestSubj="lnsVisualOptionsButton"
>
<EuiFormRow
display="columnCompressed"
label={
<span>
{i18n.translate('xpack.lens.heatmapChart.chartValueLabelVisibilityLabel', {
defaultMessage: 'Labels',
})}
</span>
}
>
<EuiButtonGroup
isFullWidth
legend={i18n.translate('xpack.lens.heatmapChart.chartValueLabelVisibilityLabel', {
defaultMessage: 'Labels',
})}
data-test-subj="lnsValueLabelsDisplay"
name="valueLabelsDisplay"
buttonSize="compressed"
options={valueLabelsOptions}
idSelected={valueLabelsOptions.find(({ value }) => value === valueLabels)!.id}
onChange={(modeId) => {
const newMode = valueLabelsOptions.find(({ id }) => id === modeId)!.value;
onValueLabelChange(newMode);
}}
/>
</EuiFormRow>
</ToolbarPopover>
);
};
1 change: 1 addition & 0 deletions x-pack/plugins/lens/public/shared_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export * from './coloring';
export { useDebouncedValue } from './debounced_value';
export * from './helpers';
export { LegendActionPopover } from './legend_action_popover';
export { ValueLabelsSettings } from './value_labels_settings';
export * from './static_header';
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 from 'react';
import { shallowWithIntl as shallow } from '@kbn/test/jest';
import { ValueLabelsSettings, VisualOptionsProps } from './value_labels_settings';

describe('Value labels Settings', () => {
let props: VisualOptionsProps;
beforeEach(() => {
props = {
onValueLabelChange: jest.fn(),
};
});

it('should not render the component if not enabled', () => {
const component = shallow(<ValueLabelsSettings {...props} isVisible={false} />);
expect(component.find('[data-test-subj="lens-value-labels-visibility-btn"]').length).toEqual(0);
});

it('should set hide as default value', () => {
const component = shallow(<ValueLabelsSettings {...props} />);
expect(
component.find('[data-test-subj="lens-value-labels-visibility-btn"]').prop('idSelected')
).toEqual(`value_labels_hide`);
});

it('should have called onValueLabelChange function on ButtonGroup change', () => {
const component = shallow(<ValueLabelsSettings {...props} />);
component
.find('[data-test-subj="lens-value-labels-visibility-btn"]')
.simulate('change', 'value_labels_inside');
expect(props.onValueLabelChange).toHaveBeenCalled();
});

it('should render the passed value if given', () => {
const component = shallow(<ValueLabelsSettings {...props} valueLabels="inside" />);
expect(
component.find('[data-test-subj="lens-value-labels-visibility-btn"]').prop('idSelected')
).toEqual(`value_labels_inside`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
import { ValueLabelConfig } from '../../common/types';

const valueLabelsOptions: Array<{
id: string;
value: ValueLabelConfig;
label: string;
'data-test-subj': string;
}> = [
{
id: `value_labels_hide`,
value: 'hide',
label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.auto', {
defaultMessage: 'Hide',
}),
'data-test-subj': 'lns_valueLabels_hide',
},
{
id: `value_labels_inside`,
value: 'inside',
label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.inside', {
defaultMessage: 'Show',
}),
'data-test-subj': 'lns_valueLabels_inside',
},
];

export interface VisualOptionsProps {
isVisible?: boolean;
valueLabels?: ValueLabelConfig;
onValueLabelChange: (newMode: ValueLabelConfig) => void;
}

export const ValueLabelsSettings: FC<VisualOptionsProps> = ({
isVisible = true,
valueLabels = 'hide',
onValueLabelChange,
}) => {
if (!isVisible) {
return null;
}
const label = i18n.translate('xpack.lens.shared.chartValueLabelVisibilityLabel', {
defaultMessage: 'Labels',
});
const isSelected =
valueLabelsOptions.find(({ value }) => value === valueLabels)?.id || 'value_labels_hide';
return (
<EuiFormRow display="columnCompressed" label={<span>{label}</span>}>
<EuiButtonGroup
isFullWidth
legend={label}
data-test-subj="lens-value-labels-visibility-btn"
name="valueLabelsDisplay"
buttonSize="compressed"
options={valueLabelsOptions}
idSelected={isSelected}
onChange={(modeId) => {
const newMode = valueLabelsOptions.find(({ id }) => id === modeId);
if (newMode) {
onValueLabelChange(newMode.value);
}
}}
/>
</EuiFormRow>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import React from 'react';
import { i18n } from '@kbn/i18n';
import { ToolbarPopover, TooltipWrapper } from '../../../shared_components';
import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../shared_components';
import { MissingValuesOptions } from './missing_values_option';
import { LineCurveOption } from './line_curve_option';
import { FillOpacityOption } from './fill_opacity_option';
Expand Down Expand Up @@ -102,14 +102,17 @@ export const VisualOptionsPopover: React.FC<VisualOptionsPopoverProps> = ({
}}
/>

<MissingValuesOptions
isValueLabelsEnabled={isValueLabelsEnabled}
isFittingEnabled={isFittingEnabled}
valueLabels={state?.valueLabels}
fittingFunction={state?.fittingFunction}
<ValueLabelsSettings
isVisible={isValueLabelsEnabled}
valueLabels={state?.valueLabels ?? 'hide'}
onValueLabelChange={(newMode) => {
setState({ ...state, valueLabels: newMode });
}}
/>

<MissingValuesOptions
isFittingEnabled={isFittingEnabled}
fittingFunction={state?.fittingFunction}
onFittingFnChange={(newVal) => {
setState({ ...state, fittingFunction: newVal });
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,23 @@

import React from 'react';
import { shallowWithIntl as shallow, mountWithIntl as mount } from '@kbn/test/jest';
import { EuiSuperSelect, EuiButtonGroup } from '@elastic/eui';
import { EuiSuperSelect } from '@elastic/eui';
import { MissingValuesOptions } from './missing_values_option';

describe('Missing values option', () => {
it('should show currently selected fitting function', () => {
const component = shallow(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'hide'}
/>
<MissingValuesOptions onFittingFnChange={jest.fn()} fittingFunction={'Carry'} />
);

expect(component.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('Carry');
});

it('should show currently selected value labels display setting', () => {
const component = mount(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'inside'}
/>
);

expect(component.find(EuiButtonGroup).prop('idSelected')).toEqual('value_labels_inside');
});

it('should show display field when enabled', () => {
const component = mount(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'inside'}
/>
);

expect(component.exists('[data-test-subj="lnsValueLabelsDisplay"]')).toEqual(true);
});

it('should hide in display value label option when disabled', () => {
const component = mount(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'inside'}
isValueLabelsEnabled={false}
/>
);

expect(component.exists('[data-test-subj="lnsValueLabelsDisplay"]')).toEqual(false);
});

it('should show the fitting option when enabled', () => {
const component = mount(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'inside'}
isFittingEnabled={true}
/>
);
Expand All @@ -82,9 +35,7 @@ describe('Missing values option', () => {
const component = mount(
<MissingValuesOptions
onFittingFnChange={jest.fn()}
onValueLabelChange={jest.fn()}
fittingFunction={'Carry'}
valueLabels={'inside'}
isFittingEnabled={false}
/>
);
Expand Down
Loading

0 comments on commit 5f21476

Please sign in to comment.