[TSVB] Long legend values support (#108023)

* [TSVB] Supports legends with long values

* Add a unit test

* Design optimization

* Revert changes

* Add the missing prop type

* Ensure that the limits are respected

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Stratoula Kalafateli 2021-08-19 11:27:18 +03:00 committed by GitHub
parent 544c41e214
commit fe08d0aa21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 186 additions and 65 deletions

View file

@ -161,6 +161,8 @@ export interface Panel {
series: Series[];
show_grid: number;
show_legend: number;
truncate_legend?: number;
max_lines_legend?: number;
time_field?: string;
time_range_mode?: string;
tooltip_mode?: TOOLTIP_MODES;

View file

@ -0,0 +1,82 @@
/*
* 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 as shallow } from '@kbn/test/jest';
jest.mock('../lib/get_default_query_language', () => ({
getDefaultQueryLanguage: () => 'kuery',
}));
import { TimeseriesPanelConfig } from './timeseries';
import { PanelConfigProps } from './types';
describe('TimeseriesPanelConfig', () => {
it('sets the number input to the given value', () => {
const props = ({
fields: {},
model: {
max_lines_legend: 2,
},
onChange: jest.fn(),
} as unknown) as PanelConfigProps;
const wrapper = shallow(<TimeseriesPanelConfig {...props} />);
wrapper.instance().setState({ selectedTab: 'options' });
expect(
wrapper.find('[data-test-subj="timeSeriesEditorDataMaxLegendLines"]').prop('value')
).toEqual(2);
});
it('switches on the truncate legend switch if the prop is set to 1 ', () => {
const props = ({
fields: {},
model: {
max_lines_legend: 2,
truncate_legend: 1,
},
onChange: jest.fn(),
} as unknown) as PanelConfigProps;
const wrapper = shallow(<TimeseriesPanelConfig {...props} />);
wrapper.instance().setState({ selectedTab: 'options' });
expect(
wrapper.find('[data-test-subj="timeSeriesEditorDataTruncateLegendSwitch"]').prop('value')
).toEqual(1);
});
it('switches off the truncate legend switch if the prop is set to 0', () => {
const props = ({
fields: {},
model: {
max_lines_legend: 2,
truncate_legend: 0,
},
onChange: jest.fn(),
} as unknown) as PanelConfigProps;
const wrapper = shallow(<TimeseriesPanelConfig {...props} />);
wrapper.instance().setState({ selectedTab: 'options' });
expect(
wrapper.find('[data-test-subj="timeSeriesEditorDataTruncateLegendSwitch"]').prop('value')
).toEqual(0);
});
it('disables the max lines number input if the truncate legend switch is off', () => {
const props = ({
fields: {},
model: {
max_lines_legend: 2,
truncate_legend: 0,
},
onChange: jest.fn(),
} as unknown) as PanelConfigProps;
const wrapper = shallow(<TimeseriesPanelConfig {...props} />);
wrapper.instance().setState({ selectedTab: 'options' });
expect(
wrapper.find('[data-test-subj="timeSeriesEditorDataMaxLegendLines"]').prop('disabled')
).toEqual(true);
});
});

View file

@ -23,6 +23,7 @@ import {
EuiFieldText,
EuiTitle,
EuiHorizontalRule,
EuiFieldNumber,
} from '@elastic/eui';
// @ts-expect-error not typed yet
@ -102,6 +103,9 @@ const legendPositionOptions = [
},
];
const MAX_TRUNCATE_LINES = 5;
const MIN_TRUNCATE_LINES = 1;
export class TimeseriesPanelConfig extends Component<
PanelConfigProps,
{ selectedTab: PANEL_CONFIG_TABS }
@ -344,7 +348,7 @@ export class TimeseriesPanelConfig extends Component<
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexItem grow={false}>
<ColorPicker
onChange={this.props.onChange}
name="background_color"
@ -352,47 +356,15 @@ export class TimeseriesPanelConfig extends Component<
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
label={i18n.translate('visTypeTimeseries.timeseries.optionsTab.showLegendLabel', {
defaultMessage: 'Show legend?',
})}
>
<YesNo
value={model.show_legend}
name="show_legend"
onChange={this.props.onChange}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel htmlFor={htmlId('legendPos')}>
<EuiFormLabel>
<FormattedMessage
id="visTypeTimeseries.timeseries.optionsTab.legendPositionLabel"
defaultMessage="Legend position"
id="visTypeTimeseries.timeseries.optionsTab.displayGridLabel"
defaultMessage="Display grid"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiComboBox
isClearable={false}
id={htmlId('legendPos')}
options={legendPositionOptions}
selectedOptions={selectedLegendPosOption ? [selectedLegendPosOption] : []}
onChange={handleSelectChange('legend_position')}
singleSelection={{ asPlainText: true }}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow
label={i18n.translate(
'visTypeTimeseries.timeseries.optionsTab.displayGridLabel',
{
defaultMessage: 'Display grid',
}
)}
>
<YesNo value={model.show_grid} name="show_grid" onChange={this.props.onChange} />
</EuiFormRow>
<YesNo value={model.show_grid} name="show_grid" onChange={this.props.onChange} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>
@ -402,7 +374,7 @@ export class TimeseriesPanelConfig extends Component<
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiComboBox
isClearable={false}
id={htmlId('tooltipMode')}
@ -413,6 +385,84 @@ export class TimeseriesPanelConfig extends Component<
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup responsive={false} wrap={true} alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel>
<FormattedMessage
id="visTypeTimeseries.timeseries.optionsTab.showLegendLabel"
defaultMessage="Show legend?"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<YesNo
value={model.show_legend}
name="show_legend"
onChange={this.props.onChange}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>
<FormattedMessage
id="visTypeTimeseries.timeseries.optionsTab.truncateLegendLabel"
defaultMessage="Truncate legend?"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<YesNo
value={model.truncate_legend}
name="truncate_legend"
onChange={this.props.onChange}
data-test-subj="timeSeriesEditorDataTruncateLegendSwitch"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel>
<FormattedMessage
id="visTypeTimeseries.timeseries.optionsTab.maxLinesLabel"
defaultMessage="Maximum legend lines"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFieldNumber
data-test-subj="timeSeriesEditorDataMaxLegendLines"
value={model.max_lines_legend}
min={MIN_TRUNCATE_LINES}
max={MAX_TRUNCATE_LINES}
compressed
disabled={!Boolean(model.truncate_legend)}
onChange={(e) => {
const val = Number(e.target.value);
this.props.onChange({
max_lines_legend: Math.min(
MAX_TRUNCATE_LINES,
Math.max(val, MIN_TRUNCATE_LINES)
),
});
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormLabel htmlFor={htmlId('legendPos')}>
<FormattedMessage
id="visTypeTimeseries.timeseries.optionsTab.legendPositionLabel"
defaultMessage="Legend position"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiComboBox
isClearable={false}
id={htmlId('legendPos')}
options={legendPositionOptions}
selectedOptions={selectedLegendPosOption ? [selectedLegendPosOption] : []}
onChange={handleSelectChange('legend_position')}
singleSelection={{ asPlainText: true }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</div>
);

View file

@ -238,6 +238,8 @@ class TimeseriesVisualization extends Component {
showGrid={Boolean(model.show_grid)}
legend={Boolean(model.show_legend)}
legendPosition={model.legend_position}
truncateLegend={Boolean(model.truncate_legend)}
maxLegendLines={model.max_lines_legend}
tooltipMode={model.tooltip_mode}
xAxisFormatter={this.xAxisFormatter(interval)}
annotations={this.prepareAnnotations()}

View file

@ -56,6 +56,8 @@ export const TimeSeries = ({
showGrid,
legend,
legendPosition,
truncateLegend,
maxLegendLines,
tooltipMode,
series,
yAxis,
@ -172,6 +174,9 @@ export const TimeSeries = ({
background: {
color: backgroundColor,
},
legend: {
labelOptions: { maxLines: truncateLegend ? maxLegendLines ?? 1 : 0 },
},
},
chartTheme,
]}
@ -216,6 +221,7 @@ export const TimeSeries = ({
lines,
data,
hideInLegend,
truncateLegend,
xScaleType,
yScaleType,
groupId,
@ -249,6 +255,7 @@ export const TimeSeries = ({
name={getValueOrEmpty(seriesName)}
data={data}
hideInLegend={hideInLegend}
truncateLegend={truncateLegend}
bars={bars}
color={finalColor}
stackAccessors={stackAccessors}
@ -274,6 +281,7 @@ export const TimeSeries = ({
name={getValueOrEmpty(seriesName)}
data={data}
hideInLegend={hideInLegend}
truncateLegend={truncateLegend}
lines={lines}
color={finalColor}
stackAccessors={stackAccessors}
@ -336,6 +344,8 @@ TimeSeries.propTypes = {
showGrid: PropTypes.bool,
legend: PropTypes.bool,
legendPosition: PropTypes.string,
truncateLegend: PropTypes.bool,
maxLegendLines: PropTypes.number,
series: PropTypes.array,
yAxis: PropTypes.array,
onBrush: PropTypes.func,

View file

@ -93,6 +93,8 @@ export const metricsVisDefinition: VisTypeDefinition<
axis_formatter: 'number',
axis_scale: 'normal',
show_legend: 1,
truncate_legend: 1,
max_lines_legend: 1,
show_grid: 1,
tooltip_mode: TOOLTIP_MODES.SHOW_ALL,
drop_last_bucket: 0,

View file

@ -2,30 +2,3 @@
# yarn lockfile v1
"@kbn/interpreter@link:../packages/kbn-interpreter":
version "0.0.0"
uid ""
"@kbn/optimizer@link:../packages/kbn-optimizer":
version "0.0.0"
uid ""
"@kbn/plugin-helpers@link:../packages/kbn-plugin-helpers":
version "0.0.0"
uid ""
"@kbn/storybook@link:../packages/kbn-storybook":
version "0.0.0"
uid ""
"@kbn/test@link:../packages/kbn-test":
version "0.0.0"
uid ""
"@kbn/ui-framework@link:../packages/kbn-ui-framework":
version "0.0.0"
uid ""
"@kbn/ui-shared-deps@link:../packages/kbn-ui-shared-deps":
version "0.0.0"
uid ""