Skip to content

Commit

Permalink
[Lens] Add support for curved, linear and stepped lines (elastic#158896)
Browse files Browse the repository at this point in the history
Adds support for `straight`, `smooth` (curved) and `step` (after) curves
on line and area charts in Lens.
  • Loading branch information
nickofthyme authored and saarikabhasi committed Jun 14, 2023
1 parent df9c194 commit 09e13fe
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/visualizations/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}}
/>
Expand Down
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 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',
}),
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

it('should show currently curving disabled', () => {
const component = shallow(<LineCurveOption onChange={jest.fn()} value={'LINEAR'} />);

expect(component.find(EuiSwitch).prop('checked')).toEqual(false);
});
expect(component.find(EuiSuperSelect).first().prop('valueOfSelected')).toEqual(type);
}
);

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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -21177,7 +21177,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",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -21177,7 +21177,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": "終了値",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -21177,7 +21177,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": "结束值",
Expand Down
2 changes: 1 addition & 1 deletion x-pack/test/functional/apps/lens/group1/smokescreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
9 changes: 6 additions & 3 deletions x-pack/test/functional/page_objects/lens_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion x-pack/test/localization/tests/lens/smokescreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand Down
3 changes: 2 additions & 1 deletion x-pack/test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}

0 comments on commit 09e13fe

Please sign in to comment.