mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Supports long legend values (#107894)
* [Lens] Supports multilines legend * Add a truncate legends switch * Add more unit tests * Add tooltip condition * Adress PR comments * Apply PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
02817055c0
commit
8939ee6c24
20 changed files with 342 additions and 3 deletions
|
@ -19,6 +19,14 @@ export interface HeatmapLegendConfig {
|
|||
* Position of the legend relative to the chart
|
||||
*/
|
||||
position: Position;
|
||||
/**
|
||||
* Defines the number of lines per legend item
|
||||
*/
|
||||
maxLines?: number;
|
||||
/**
|
||||
* Defines if the legend items should be truncated
|
||||
*/
|
||||
shouldTruncate?: boolean;
|
||||
}
|
||||
|
||||
export type HeatmapLegendConfigResult = HeatmapLegendConfig & {
|
||||
|
@ -54,6 +62,19 @@ export const heatmapLegendConfig: ExpressionFunctionDefinition<
|
|||
defaultMessage: 'Specifies the legend position.',
|
||||
}),
|
||||
},
|
||||
maxLines: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.maxLines.help', {
|
||||
defaultMessage: 'Specifies the number of lines per legend item.',
|
||||
}),
|
||||
},
|
||||
shouldTruncate: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.shouldTruncate.help', {
|
||||
defaultMessage: 'Specifies whether or not the legend items should be truncated.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
return {
|
||||
|
|
|
@ -74,6 +74,14 @@ export const pie: ExpressionFunctionDefinition<
|
|||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
legendMaxLines: {
|
||||
types: ['number'],
|
||||
help: '',
|
||||
},
|
||||
truncateLegend: {
|
||||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
legendPosition: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
|
|
|
@ -17,6 +17,8 @@ export interface SharedPieLayerState {
|
|||
legendPosition?: 'left' | 'right' | 'top' | 'bottom';
|
||||
nestedLegend?: boolean;
|
||||
percentDecimals?: number;
|
||||
legendMaxLines?: number;
|
||||
truncateLegend?: boolean;
|
||||
}
|
||||
|
||||
export type PieLayerState = SharedPieLayerState & {
|
||||
|
|
|
@ -37,6 +37,14 @@ export interface LegendConfig {
|
|||
* Number of columns when legend is set inside chart
|
||||
*/
|
||||
floatingColumns?: number;
|
||||
/**
|
||||
* Maximum number of lines per legend item
|
||||
*/
|
||||
maxLines?: number;
|
||||
/**
|
||||
* Flag whether the legend items are truncated or not
|
||||
*/
|
||||
shouldTruncate?: boolean;
|
||||
}
|
||||
|
||||
export type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' };
|
||||
|
@ -100,6 +108,19 @@ export const legendConfig: ExpressionFunctionDefinition<
|
|||
defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.',
|
||||
}),
|
||||
},
|
||||
maxLines: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.xyChart.maxLines.help', {
|
||||
defaultMessage: 'Specifies the number of lines per legend item.',
|
||||
}),
|
||||
},
|
||||
shouldTruncate: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('xpack.lens.xyChart.shouldTruncate.help', {
|
||||
defaultMessage: 'Specifies whether the legend items will be truncated or not',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: LegendConfig) {
|
||||
return {
|
||||
|
|
|
@ -321,6 +321,12 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = ({
|
|||
showLegend={args.legend.isVisible}
|
||||
legendPosition={args.legend.position}
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
theme={{
|
||||
...chartTheme,
|
||||
legend: {
|
||||
labelOptions: { maxLines: args.legend.shouldTruncate ? args.legend?.maxLines ?? 1 : 0 },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Heatmap
|
||||
id={tableId}
|
||||
|
|
|
@ -65,6 +65,21 @@ export const HeatmapToolbar = memo(
|
|||
legend: { ...state.legend, position: id as Position },
|
||||
});
|
||||
}}
|
||||
maxLines={state?.legend.maxLines}
|
||||
onMaxLinesChange={(val) => {
|
||||
setState({
|
||||
...state,
|
||||
legend: { ...state.legend, maxLines: val },
|
||||
});
|
||||
}}
|
||||
shouldTruncate={state?.legend.shouldTruncate ?? true}
|
||||
onTruncateLegendChange={() => {
|
||||
const current = state.legend.shouldTruncate ?? true;
|
||||
setState({
|
||||
...state,
|
||||
legend: { ...state.legend, shouldTruncate: !current },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -32,6 +32,8 @@ function exampleState(): HeatmapVisualizationState {
|
|||
isVisible: true,
|
||||
position: Position.Right,
|
||||
type: LEGEND_FUNCTION,
|
||||
maxLines: 1,
|
||||
shouldTruncate: true,
|
||||
},
|
||||
gridConfig: {
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
|
@ -63,6 +65,8 @@ describe('heatmap', () => {
|
|||
isVisible: true,
|
||||
position: Position.Right,
|
||||
type: LEGEND_FUNCTION,
|
||||
maxLines: 1,
|
||||
shouldTruncate: true,
|
||||
},
|
||||
gridConfig: {
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
|
|
|
@ -70,6 +70,8 @@ function getInitialState(): Omit<HeatmapVisualizationState, 'layerId' | 'layerTy
|
|||
legend: {
|
||||
isVisible: true,
|
||||
position: Position.Right,
|
||||
maxLines: 1,
|
||||
shouldTruncate: true,
|
||||
type: LEGEND_FUNCTION,
|
||||
},
|
||||
gridConfig: {
|
||||
|
|
|
@ -62,6 +62,8 @@ describe('PieVisualization component', () => {
|
|||
numberDisplay: 'hidden',
|
||||
categoryDisplay: 'default',
|
||||
legendDisplay: 'default',
|
||||
legendMaxLines: 1,
|
||||
truncateLegend: true,
|
||||
nestedLegend: false,
|
||||
percentDecimals: 3,
|
||||
hideLabels: false,
|
||||
|
@ -106,6 +108,20 @@ describe('PieVisualization component', () => {
|
|||
expect(component.find(Settings).prop('showLegend')).toEqual(false);
|
||||
});
|
||||
|
||||
test('it sets the correct lines per legend item', () => {
|
||||
const component = shallow(<PieComponent args={args} {...getDefaultArgs()} />);
|
||||
expect(component.find(Settings).prop('theme')).toEqual({
|
||||
background: {
|
||||
color: undefined,
|
||||
},
|
||||
legend: {
|
||||
labelOptions: {
|
||||
maxLines: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it calls the color function with the right series layers', () => {
|
||||
const defaultArgs = getDefaultArgs();
|
||||
const component = shallow(
|
||||
|
|
|
@ -75,6 +75,8 @@ export function PieComponent(
|
|||
legendPosition,
|
||||
nestedLegend,
|
||||
percentDecimals,
|
||||
legendMaxLines,
|
||||
truncateLegend,
|
||||
hideLabels,
|
||||
palette,
|
||||
} = props.args;
|
||||
|
@ -297,6 +299,9 @@ export function PieComponent(
|
|||
...chartTheme.background,
|
||||
color: undefined, // removes background for embeddables
|
||||
},
|
||||
legend: {
|
||||
labelOptions: { maxLines: truncateLegend ? legendMaxLines ?? 1 : 0 },
|
||||
},
|
||||
}}
|
||||
baseTheme={chartBaseTheme}
|
||||
/>
|
||||
|
|
|
@ -494,6 +494,8 @@ describe('suggestions', () => {
|
|||
categoryDisplay: 'inside',
|
||||
legendDisplay: 'show',
|
||||
percentDecimals: 0,
|
||||
legendMaxLines: 1,
|
||||
truncateLegend: true,
|
||||
nestedLegend: true,
|
||||
},
|
||||
],
|
||||
|
@ -516,6 +518,8 @@ describe('suggestions', () => {
|
|||
categoryDisplay: 'inside',
|
||||
legendDisplay: 'show',
|
||||
percentDecimals: 0,
|
||||
legendMaxLines: 1,
|
||||
truncateLegend: true,
|
||||
nestedLegend: true,
|
||||
},
|
||||
],
|
||||
|
@ -684,6 +688,8 @@ describe('suggestions', () => {
|
|||
categoryDisplay: 'inside',
|
||||
legendDisplay: 'show',
|
||||
percentDecimals: 0,
|
||||
legendMaxLines: 1,
|
||||
truncateLegend: true,
|
||||
nestedLegend: true,
|
||||
},
|
||||
],
|
||||
|
@ -705,6 +711,8 @@ describe('suggestions', () => {
|
|||
categoryDisplay: 'default', // This is changed
|
||||
legendDisplay: 'show',
|
||||
percentDecimals: 0,
|
||||
legendMaxLines: 1,
|
||||
truncateLegend: true,
|
||||
nestedLegend: true,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -56,6 +56,8 @@ function expressionHelper(
|
|||
legendDisplay: [layer.legendDisplay],
|
||||
legendPosition: [layer.legendPosition || 'right'],
|
||||
percentDecimals: [layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS],
|
||||
legendMaxLines: [layer.legendMaxLines ?? 1],
|
||||
truncateLegend: [layer.truncateLegend ?? true],
|
||||
nestedLegend: [!!layer.nestedLegend],
|
||||
...(state.palette
|
||||
? {
|
||||
|
|
|
@ -220,6 +220,21 @@ export function PieToolbar(props: VisualizationToolbarProps<PieVisualizationStat
|
|||
layers: [{ ...layer, nestedLegend: !layer.nestedLegend }],
|
||||
});
|
||||
}}
|
||||
shouldTruncate={layer.truncateLegend ?? true}
|
||||
onTruncateLegendChange={() => {
|
||||
const current = layer.truncateLegend ?? true;
|
||||
setState({
|
||||
...state,
|
||||
layers: [{ ...layer, truncateLegend: !current }],
|
||||
});
|
||||
}}
|
||||
maxLines={layer?.legendMaxLines}
|
||||
onMaxLinesChange={(val) => {
|
||||
setState({
|
||||
...state,
|
||||
layers: [{ ...layer, legendMaxLines: val }],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl as shallow } from '@kbn/test/jest';
|
||||
import { LegendSettingsPopover, LegendSettingsPopoverProps } from './legend_settings_popover';
|
||||
import {
|
||||
LegendSettingsPopover,
|
||||
LegendSettingsPopoverProps,
|
||||
MaxLinesInput,
|
||||
} from './legend_settings_popover';
|
||||
|
||||
describe('Legend Settings', () => {
|
||||
const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [
|
||||
|
@ -50,6 +54,41 @@ describe('Legend Settings', () => {
|
|||
expect(props.onDisplayChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have default the max lines input to 1 when no value is given', () => {
|
||||
const component = shallow(<LegendSettingsPopover {...props} />);
|
||||
expect(component.find(MaxLinesInput).prop('value')).toEqual(1);
|
||||
});
|
||||
|
||||
it('should have the `Truncate legend text` switch enabled by default', () => {
|
||||
const component = shallow(<LegendSettingsPopover {...props} />);
|
||||
expect(
|
||||
component.find('[data-test-subj="lens-legend-truncate-switch"]').prop('checked')
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
it('should set the truncate switch state when truncate prop value is false', () => {
|
||||
const component = shallow(<LegendSettingsPopover {...props} shouldTruncate={false} />);
|
||||
expect(
|
||||
component.find('[data-test-subj="lens-legend-truncate-switch"]').prop('checked')
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('should have disabled the max lines input when truncate is set to false', () => {
|
||||
const component = shallow(<LegendSettingsPopover {...props} shouldTruncate={false} />);
|
||||
expect(component.find(MaxLinesInput).prop('isDisabled')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should have called the onTruncateLegendChange function on truncate switch change', () => {
|
||||
const nestedProps = {
|
||||
...props,
|
||||
shouldTruncate: true,
|
||||
onTruncateLegendChange: jest.fn(),
|
||||
};
|
||||
const component = shallow(<LegendSettingsPopover {...nestedProps} />);
|
||||
component.find('[data-test-subj="lens-legend-truncate-switch"]').simulate('change');
|
||||
expect(nestedProps.onTruncateLegendChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should enable the Nested Legend Switch when renderNestedLegendSwitch prop is true', () => {
|
||||
const component = shallow(<LegendSettingsPopover {...props} renderNestedLegendSwitch />);
|
||||
expect(component.find('[data-test-subj="lens-legend-nested-switch"]')).toHaveLength(1);
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFormRow, EuiButtonGroup, EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
|
||||
import {
|
||||
EuiFormRow,
|
||||
EuiButtonGroup,
|
||||
EuiSwitch,
|
||||
EuiSwitchEvent,
|
||||
EuiFieldNumber,
|
||||
} from '@elastic/eui';
|
||||
import { Position, VerticalAlignment, HorizontalAlignment } from '@elastic/charts';
|
||||
import { ToolbarPopover } from '../shared_components';
|
||||
import { LegendLocationSettings } from './legend_location_settings';
|
||||
import { ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { TooltipWrapper } from './tooltip_wrapper';
|
||||
import { useDebouncedValue } from './debounced_value';
|
||||
|
||||
export interface LegendSettingsPopoverProps {
|
||||
/**
|
||||
|
@ -64,9 +71,25 @@ export interface LegendSettingsPopoverProps {
|
|||
*/
|
||||
floatingColumns?: number;
|
||||
/**
|
||||
* Callback on horizontal alignment option change
|
||||
* Callback on alignment option change
|
||||
*/
|
||||
onFloatingColumnsChange?: (value: number) => void;
|
||||
/**
|
||||
* Sets the number of lines per legend item
|
||||
*/
|
||||
maxLines?: number;
|
||||
/**
|
||||
* Callback on max lines option change
|
||||
*/
|
||||
onMaxLinesChange?: (value: number) => void;
|
||||
/**
|
||||
* Defines if the legend items will be truncated or not
|
||||
*/
|
||||
shouldTruncate?: boolean;
|
||||
/**
|
||||
* Callback on nested switch status change
|
||||
*/
|
||||
onTruncateLegendChange?: (event: EuiSwitchEvent) => void;
|
||||
/**
|
||||
* If true, nested legend switch is rendered
|
||||
*/
|
||||
|
@ -97,6 +120,38 @@ export interface LegendSettingsPopoverProps {
|
|||
groupPosition?: ToolbarButtonProps['groupPosition'];
|
||||
}
|
||||
|
||||
const DEFAULT_TRUNCATE_LINES = 1;
|
||||
const MAX_TRUNCATE_LINES = 5;
|
||||
const MIN_TRUNCATE_LINES = 1;
|
||||
|
||||
export const MaxLinesInput = ({
|
||||
value,
|
||||
setValue,
|
||||
isDisabled,
|
||||
}: {
|
||||
value: number;
|
||||
setValue: (value: number) => void;
|
||||
isDisabled: boolean;
|
||||
}) => {
|
||||
const { inputValue, handleInputChange } = useDebouncedValue({ value, onChange: setValue });
|
||||
return (
|
||||
<EuiFieldNumber
|
||||
data-test-subj="lens-legend-max-lines-input"
|
||||
value={inputValue}
|
||||
min={MIN_TRUNCATE_LINES}
|
||||
max={MAX_TRUNCATE_LINES}
|
||||
compressed
|
||||
disabled={isDisabled}
|
||||
onChange={(e) => {
|
||||
const val = Number(e.target.value);
|
||||
// we want to automatically change the values to the limits
|
||||
// if the user enters a value that is outside the limits
|
||||
handleInputChange(Math.min(MAX_TRUNCATE_LINES, Math.max(val, MIN_TRUNCATE_LINES)));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const LegendSettingsPopover: React.FunctionComponent<LegendSettingsPopoverProps> = ({
|
||||
legendOptions,
|
||||
mode,
|
||||
|
@ -117,6 +172,10 @@ export const LegendSettingsPopover: React.FunctionComponent<LegendSettingsPopove
|
|||
onValueInLegendChange = () => {},
|
||||
renderValueInLegendSwitch,
|
||||
groupPosition = 'right',
|
||||
maxLines,
|
||||
onMaxLinesChange = () => {},
|
||||
shouldTruncate,
|
||||
onTruncateLegendChange = () => {},
|
||||
}) => {
|
||||
return (
|
||||
<ToolbarPopover
|
||||
|
@ -158,6 +217,63 @@ export const LegendSettingsPopover: React.FunctionComponent<LegendSettingsPopove
|
|||
position={position}
|
||||
onPositionChange={onPositionChange}
|
||||
/>
|
||||
<EuiFormRow
|
||||
display="columnCompressedSwitch"
|
||||
label={i18n.translate('xpack.lens.shared.truncateLegend', {
|
||||
defaultMessage: 'Truncate text',
|
||||
})}
|
||||
>
|
||||
<TooltipWrapper
|
||||
tooltipContent={i18n.translate('xpack.lens.shared.legendVisibleTooltip', {
|
||||
defaultMessage: 'Requires legend to be shown',
|
||||
})}
|
||||
condition={mode === 'hide'}
|
||||
position="top"
|
||||
delay="regular"
|
||||
display="block"
|
||||
>
|
||||
<EuiSwitch
|
||||
compressed
|
||||
label={i18n.translate('xpack.lens.shared.truncateLegend', {
|
||||
defaultMessage: 'Truncate text',
|
||||
})}
|
||||
data-test-subj="lens-legend-truncate-switch"
|
||||
showLabel={false}
|
||||
disabled={mode === 'hide'}
|
||||
checked={shouldTruncate ?? true}
|
||||
onChange={onTruncateLegendChange}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.lens.shared.maxLinesLabel', {
|
||||
defaultMessage: 'Maximum lines',
|
||||
})}
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
>
|
||||
<TooltipWrapper
|
||||
tooltipContent={
|
||||
mode === 'hide'
|
||||
? i18n.translate('xpack.lens.shared.legendVisibleTooltip', {
|
||||
defaultMessage: 'Requires legend to be shown',
|
||||
})
|
||||
: i18n.translate('xpack.lens.shared.legendIsTruncated', {
|
||||
defaultMessage: 'Requires text to be truncated',
|
||||
})
|
||||
}
|
||||
condition={mode === 'hide' || !shouldTruncate}
|
||||
position="top"
|
||||
delay="regular"
|
||||
display="block"
|
||||
>
|
||||
<MaxLinesInput
|
||||
value={maxLines ?? DEFAULT_TRUNCATE_LINES}
|
||||
setValue={onMaxLinesChange}
|
||||
isDisabled={mode === 'hide' || !shouldTruncate}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</EuiFormRow>
|
||||
{renderNestedLegendSwitch && (
|
||||
<EuiFormRow
|
||||
display="columnCompressedSwitch"
|
||||
|
|
|
@ -27,6 +27,11 @@ exports[`xy_expression XYChart component it renders area 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -245,6 +250,11 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -477,6 +487,11 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -709,6 +724,11 @@ exports[`xy_expression XYChart component it renders line 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -927,6 +947,11 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -1153,6 +1178,11 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
@ -1393,6 +1423,11 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
|
|||
"color": undefined,
|
||||
},
|
||||
"barSeriesStyle": Object {},
|
||||
"legend": Object {
|
||||
"labelOptions": Object {
|
||||
"maxLines": 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tooltip={
|
||||
|
|
|
@ -145,9 +145,13 @@ Object {
|
|||
"isVisible": Array [
|
||||
true,
|
||||
],
|
||||
"maxLines": Array [],
|
||||
"position": Array [
|
||||
"bottom",
|
||||
],
|
||||
"shouldTruncate": Array [
|
||||
true,
|
||||
],
|
||||
"showSingleSeries": Array [],
|
||||
"verticalAlignment": Array [],
|
||||
},
|
||||
|
|
|
@ -517,6 +517,9 @@ export function XYChart({
|
|||
background: {
|
||||
color: undefined, // removes background for embeddables
|
||||
},
|
||||
legend: {
|
||||
labelOptions: { maxLines: legend.shouldTruncate ? legend?.maxLines ?? 1 : 0 },
|
||||
},
|
||||
}}
|
||||
baseTheme={chartBaseTheme}
|
||||
tooltip={{
|
||||
|
|
|
@ -156,6 +156,8 @@ export const buildExpression = (
|
|||
floatingColumns: state.legend.floatingColumns
|
||||
? [Math.min(5, state.legend.floatingColumns)]
|
||||
: [],
|
||||
maxLines: state.legend.maxLines ? [state.legend.maxLines] : [],
|
||||
shouldTruncate: [state.legend.shouldTruncate ?? true],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -480,6 +480,21 @@ export const XyToolbar = memo(function XyToolbar(props: VisualizationToolbarProp
|
|||
legend: { ...state.legend, floatingColumns: val },
|
||||
});
|
||||
}}
|
||||
maxLines={state?.legend.maxLines}
|
||||
onMaxLinesChange={(val) => {
|
||||
setState({
|
||||
...state,
|
||||
legend: { ...state.legend, maxLines: val },
|
||||
});
|
||||
}}
|
||||
shouldTruncate={state?.legend.shouldTruncate ?? true}
|
||||
onTruncateLegendChange={() => {
|
||||
const current = state?.legend.shouldTruncate ?? true;
|
||||
setState({
|
||||
...state,
|
||||
legend: { ...state.legend, shouldTruncate: !current },
|
||||
});
|
||||
}}
|
||||
onPositionChange={(id) => {
|
||||
setState({
|
||||
...state,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue