mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Add support for curved, linear and stepped lines (#158896)
Adds support for `straight`, `smooth` (curved) and `step` (after) curves on line and area charts in Lens.
This commit is contained in:
parent
48e783972a
commit
afdf3f64fd
15 changed files with 110 additions and 61 deletions
|
@ -9,7 +9,7 @@
|
|||
export const PLUGIN_ID = 'expressionXy';
|
||||
export const PLUGIN_NAME = 'expressionXy';
|
||||
|
||||
export { LayerTypes } from './constants';
|
||||
export { LayerTypes, XYCurveTypes } from './constants';
|
||||
|
||||
export type {
|
||||
AllowedXYOverrides,
|
||||
|
|
|
@ -14,6 +14,6 @@ export function plugin() {
|
|||
return new ExpressionXyPlugin();
|
||||
}
|
||||
|
||||
export { LayerTypes } from '../common';
|
||||
export { LayerTypes, XYCurveTypes } from '../common';
|
||||
|
||||
export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types';
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
*/
|
||||
|
||||
import { Position } from '@elastic/charts';
|
||||
import { XYConfiguration, XYLayerConfig } from '@kbn/visualizations-plugin/common';
|
||||
import { XYCurveTypes } from '@kbn/visualizations-plugin/public';
|
||||
import type { XYConfiguration, XYLayerConfig } from '@kbn/visualizations-plugin/common';
|
||||
import { Panel } from '../../../../../common/types';
|
||||
import { getYExtents } from './extents';
|
||||
|
||||
|
@ -18,6 +19,7 @@ export const getConfigurationForTimeseries = (
|
|||
const extents = getYExtents(model);
|
||||
return {
|
||||
layers,
|
||||
curveType: model.series[0].steps === 1 ? XYCurveTypes.CURVE_STEP_AFTER : undefined,
|
||||
fillOpacity: Number(model.series[0].fill) ?? 0.3,
|
||||
legend: {
|
||||
isVisible: Boolean(model.show_legend),
|
||||
|
|
|
@ -53,7 +53,7 @@ export {
|
|||
DEFAULT_LEGEND_SIZE,
|
||||
} from '../common/constants';
|
||||
export type { SavedVisState, VisParams, Dimension } from '../common';
|
||||
export { prepareLogTable } from '../common';
|
||||
export { prepareLogTable, XYCurveTypes } from '../common';
|
||||
export type { ExpressionValueVisDimension } from '../common/expression_functions/vis_dimension';
|
||||
export type {
|
||||
ExpressionValueXYDimension,
|
||||
|
|
|
@ -88,12 +88,12 @@ export const VisualOptionsPopover: React.FC<VisualOptionsPopoverProps> = ({
|
|||
isDisabled={isDisabled}
|
||||
>
|
||||
<LineCurveOption
|
||||
isCurveTypeEnabled={isCurveTypeEnabled}
|
||||
enabled={isCurveTypeEnabled}
|
||||
value={state?.curveType}
|
||||
onChange={(id) => {
|
||||
onChange={(curveType) => {
|
||||
setState({
|
||||
...state,
|
||||
curveType: id,
|
||||
curveType,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -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 type { XYCurveType } from '@kbn/expression-xy-plugin/common';
|
||||
import { XYCurveTypes } from '@kbn/expression-xy-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export interface LineCurveDefinitions {
|
||||
type: Extract<XYCurveType, 'LINEAR' | 'CURVE_MONOTONE_X' | 'CURVE_STEP_AFTER'>;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const lineCurveDefinitions: LineCurveDefinitions[] = [
|
||||
{
|
||||
type: XYCurveTypes.LINEAR,
|
||||
title: i18n.translate('xpack.lens.lineCurve.straight', {
|
||||
defaultMessage: 'Straight',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.lineCurveDescription.straight', {
|
||||
defaultMessage: 'Straight line between points',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: XYCurveTypes.CURVE_MONOTONE_X,
|
||||
title: i18n.translate('xpack.lens.lineCurve.smooth', {
|
||||
defaultMessage: 'Smooth',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.lineCurveDescription.smooth', {
|
||||
defaultMessage: 'Smoothed line between points',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: XYCurveTypes.CURVE_STEP_AFTER,
|
||||
title: i18n.translate('xpack.lens.lineCurve.step', {
|
||||
defaultMessage: 'Step',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.lineCurveDescription.step', {
|
||||
defaultMessage: 'Stepped line between points',
|
||||
}),
|
||||
},
|
||||
];
|
|
@ -7,35 +7,33 @@
|
|||
|
||||
import React from 'react';
|
||||
import { mountWithIntl as mount, shallowWithIntl as shallow } from '@kbn/test-jest-helpers';
|
||||
import { EuiSwitch } from '@elastic/eui';
|
||||
import { EuiSuperSelect } from '@elastic/eui';
|
||||
import { LineCurveOption } from './line_curve_option';
|
||||
import { lineCurveDefinitions } from './line_curve_definitions';
|
||||
|
||||
describe('Line curve option', () => {
|
||||
it('should show currently selected line curve option', () => {
|
||||
const component = shallow(<LineCurveOption onChange={jest.fn()} value={'CURVE_MONOTONE_X'} />);
|
||||
it.each(lineCurveDefinitions.map((v) => v.type))(
|
||||
'should show currently line curve option - %s',
|
||||
(type) => {
|
||||
const component = shallow(<LineCurveOption onChange={jest.fn()} value={type} />);
|
||||
|
||||
expect(component.find(EuiSwitch).prop('checked')).toEqual(true);
|
||||
});
|
||||
expect(component.find(EuiSuperSelect).first().prop('valueOfSelected')).toEqual(type);
|
||||
}
|
||||
);
|
||||
|
||||
it('should show currently curving disabled', () => {
|
||||
const component = shallow(<LineCurveOption onChange={jest.fn()} value={'LINEAR'} />);
|
||||
|
||||
expect(component.find(EuiSwitch).prop('checked')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should show curving option when enabled', () => {
|
||||
it('should show line curve option when enabled', () => {
|
||||
const component = mount(
|
||||
<LineCurveOption onChange={jest.fn()} value={'LINEAR'} isCurveTypeEnabled={true} />
|
||||
<LineCurveOption onChange={jest.fn()} value={'LINEAR'} enabled={true} />
|
||||
);
|
||||
|
||||
expect(component.exists('[data-test-subj="lnsCurveStyleToggle"]')).toEqual(true);
|
||||
expect(component.exists('[data-test-subj="lnsCurveStyleSelect"]')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should hide curve option when disabled', () => {
|
||||
it('should hide line curve option when disabled', () => {
|
||||
const component = mount(
|
||||
<LineCurveOption onChange={jest.fn()} value={'LINEAR'} isCurveTypeEnabled={false} />
|
||||
<LineCurveOption onChange={jest.fn()} value={'LINEAR'} enabled={false} />
|
||||
);
|
||||
|
||||
expect(component.exists('[data-test-subj="lnsCurveStyleToggle"]')).toEqual(false);
|
||||
expect(component.exists('[data-test-subj="lnsCurveStyleSelect"]')).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,46 +7,48 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
|
||||
import { EuiFormRow, EuiSuperSelect, EuiText } from '@elastic/eui';
|
||||
import type { XYCurveType } from '@kbn/expression-xy-plugin/common';
|
||||
import { XYCurveTypes } from '@kbn/expression-xy-plugin/public';
|
||||
import { lineCurveDefinitions } from './line_curve_definitions';
|
||||
|
||||
export interface LineCurveOptionProps {
|
||||
/**
|
||||
* Currently selected value
|
||||
*/
|
||||
value?: XYCurveType;
|
||||
/**
|
||||
* Callback on display option change
|
||||
*/
|
||||
onChange: (id: XYCurveType) => void;
|
||||
isCurveTypeEnabled?: boolean;
|
||||
onChange: (type: XYCurveType) => void;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export const LineCurveOption: React.FC<LineCurveOptionProps> = ({
|
||||
onChange,
|
||||
value,
|
||||
isCurveTypeEnabled = true,
|
||||
value = XYCurveTypes.LINEAR,
|
||||
enabled = true,
|
||||
}) => {
|
||||
return isCurveTypeEnabled ? (
|
||||
return enabled ? (
|
||||
<EuiFormRow
|
||||
display="columnCompressedSwitch"
|
||||
label={i18n.translate('xpack.lens.xyChart.curveStyleLabel', {
|
||||
defaultMessage: 'Curve lines',
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.lens.xyChart.lineInterpolationLabel', {
|
||||
defaultMessage: 'Line interpolation',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
showLabel={false}
|
||||
label="Curved"
|
||||
checked={value === 'CURVE_MONOTONE_X'}
|
||||
compressed={true}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
onChange('CURVE_MONOTONE_X');
|
||||
} else {
|
||||
onChange('LINEAR');
|
||||
}
|
||||
}}
|
||||
data-test-subj="lnsCurveStyleToggle"
|
||||
<EuiSuperSelect
|
||||
data-test-subj="lnsCurveStyleSelect"
|
||||
compressed
|
||||
options={lineCurveDefinitions.map(({ type, title, description }) => ({
|
||||
value: type,
|
||||
dropdownDisplay: (
|
||||
<>
|
||||
<strong>{title}</strong>
|
||||
<EuiText size="xs" color="subdued">
|
||||
<p>{description}</p>
|
||||
</EuiText>
|
||||
</>
|
||||
),
|
||||
inputDisplay: title,
|
||||
}))}
|
||||
valueOfSelected={value}
|
||||
onChange={onChange}
|
||||
itemLayoutAlign="top"
|
||||
hasDividers
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : null;
|
||||
|
|
|
@ -21188,7 +21188,6 @@
|
|||
"xpack.lens.xyChart.axisSide.top": "Haut",
|
||||
"xpack.lens.xyChart.bottomAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe du bas est activé.",
|
||||
"xpack.lens.xyChart.bottomAxisLabel": "Axe du bas",
|
||||
"xpack.lens.xyChart.curveStyleLabel": "Courbes",
|
||||
"xpack.lens.xyChart.defaultAnnotationLabel": "Événement",
|
||||
"xpack.lens.xyChart.defaultRangeAnnotationLabel": "Plage d'événements",
|
||||
"xpack.lens.xyChart.endValuesLabel": "Valeurs de fin",
|
||||
|
|
|
@ -21188,7 +21188,6 @@
|
|||
"xpack.lens.xyChart.axisSide.top": "トップ",
|
||||
"xpack.lens.xyChart.bottomAxisDisabledHelpText": "この設定は、下の軸が有効であるときにのみ適用されます。",
|
||||
"xpack.lens.xyChart.bottomAxisLabel": "下の軸",
|
||||
"xpack.lens.xyChart.curveStyleLabel": "曲線",
|
||||
"xpack.lens.xyChart.defaultAnnotationLabel": "イベント",
|
||||
"xpack.lens.xyChart.defaultRangeAnnotationLabel": "イベント範囲",
|
||||
"xpack.lens.xyChart.endValuesLabel": "終了値",
|
||||
|
|
|
@ -21188,7 +21188,6 @@
|
|||
"xpack.lens.xyChart.axisSide.top": "顶部",
|
||||
"xpack.lens.xyChart.bottomAxisDisabledHelpText": "此设置仅在启用底轴时应用。",
|
||||
"xpack.lens.xyChart.bottomAxisLabel": "底轴",
|
||||
"xpack.lens.xyChart.curveStyleLabel": "曲线",
|
||||
"xpack.lens.xyChart.defaultAnnotationLabel": "事件",
|
||||
"xpack.lens.xyChart.defaultRangeAnnotationLabel": "事件范围",
|
||||
"xpack.lens.xyChart.endValuesLabel": "结束值",
|
||||
|
|
|
@ -161,7 +161,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.editDimensionColor('#ff0000');
|
||||
await PageObjects.lens.openVisualOptions();
|
||||
|
||||
await PageObjects.lens.useCurvedLines();
|
||||
await PageObjects.lens.setCurvedLines('CURVE_MONOTONE_X');
|
||||
await PageObjects.lens.editMissingValues('Linear');
|
||||
|
||||
await PageObjects.lens.assertMissingValues('Linear');
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { setTimeout as setTimeoutAsync } from 'timers/promises';
|
||||
import type { FittingFunction, XYCurveType } from '@kbn/lens-plugin/public';
|
||||
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
import { logWrapper } from './log_wrapper';
|
||||
|
@ -787,10 +788,12 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
expect(await (await testSubjects.find(input)).getAttribute('value')).to.eql(value);
|
||||
});
|
||||
},
|
||||
async useCurvedLines() {
|
||||
await testSubjects.click('lnsCurveStyleToggle');
|
||||
async setCurvedLines(option: XYCurveType) {
|
||||
await testSubjects.click('lnsCurveStyleSelect');
|
||||
const optionSelector = await find.byCssSelector(`#${option}`);
|
||||
await optionSelector.click();
|
||||
},
|
||||
async editMissingValues(option: string) {
|
||||
async editMissingValues(option: FittingFunction) {
|
||||
await testSubjects.click('lnsMissingValuesSelect');
|
||||
const optionSelector = await find.byCssSelector(`#${option}`);
|
||||
await optionSelector.click();
|
||||
|
|
|
@ -305,7 +305,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.editDimensionColor('#ff0000');
|
||||
await PageObjects.lens.openVisualOptions();
|
||||
|
||||
await PageObjects.lens.useCurvedLines();
|
||||
await PageObjects.lens.setCurvedLines('CURVE_MONOTONE_X');
|
||||
await PageObjects.lens.editMissingValues('Linear');
|
||||
|
||||
await PageObjects.lens.assertMissingValues(termTranslator('Linear'));
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
"@kbn/server-route-repository",
|
||||
"@kbn/core-saved-objects-common",
|
||||
"@kbn/core-http-common",
|
||||
"@kbn/slo-schema"
|
||||
"@kbn/slo-schema",
|
||||
"@kbn/lens-plugin"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue