Skip to content

Commit bbda3f9

Browse files
authored
[Lens] Fitting functions (elastic#69820)
1 parent e70fcc7 commit bbda3f9

File tree

15 files changed

+379
-37
lines changed

15 files changed

+379
-37
lines changed

x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,29 +68,33 @@ export function WorkspacePanelWrapper({
6868
return (
6969
<EuiFlexGroup gutterSize="s" direction="column" alignItems="stretch" responsive={false}>
7070
<EuiFlexItem grow={false}>
71-
<ChartSwitch
72-
data-test-subj="lnsChartSwitcher"
73-
visualizationMap={visualizationMap}
74-
visualizationId={visualizationId}
75-
visualizationState={visualizationState}
76-
datasourceMap={datasourceMap}
77-
datasourceStates={datasourceStates}
78-
dispatch={dispatch}
79-
framePublicAPI={framePublicAPI}
80-
/>
71+
<EuiFlexGroup gutterSize="s" direction="row" responsive={false}>
72+
<EuiFlexItem grow={false}>
73+
<ChartSwitch
74+
data-test-subj="lnsChartSwitcher"
75+
visualizationMap={visualizationMap}
76+
visualizationId={visualizationId}
77+
visualizationState={visualizationState}
78+
datasourceMap={datasourceMap}
79+
datasourceStates={datasourceStates}
80+
dispatch={dispatch}
81+
framePublicAPI={framePublicAPI}
82+
/>
83+
</EuiFlexItem>
84+
{activeVisualization && activeVisualization.renderToolbar && (
85+
<EuiFlexItem grow>
86+
<NativeRenderer
87+
render={activeVisualization.renderToolbar}
88+
nativeProps={{
89+
frame: framePublicAPI,
90+
state: visualizationState,
91+
setState: setVisualizationState,
92+
}}
93+
/>
94+
</EuiFlexItem>
95+
)}
96+
</EuiFlexGroup>
8197
</EuiFlexItem>
82-
{activeVisualization && activeVisualization.renderToolbar && (
83-
<EuiFlexItem grow={false}>
84-
<NativeRenderer
85-
render={activeVisualization.renderToolbar}
86-
nativeProps={{
87-
frame: framePublicAPI,
88-
state: visualizationState,
89-
setState: setVisualizationState,
90-
}}
91-
/>
92-
</EuiFlexItem>
93-
)}
9498
<EuiFlexItem>
9599
<EuiPageContent className="lnsWorkspacePanelWrapper">
96100
{(!emptyExpression || title) && (

x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugins/lens/public/xy_visualization/__snapshots__/xy_expression.test.tsx.snap

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { Fit } from '@elastic/charts';
8+
import { i18n } from '@kbn/i18n';
9+
10+
export type FittingFunction = typeof fittingFunctionDefinitions[number]['id'];
11+
12+
export const fittingFunctionDefinitions = [
13+
{
14+
id: 'None',
15+
title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', {
16+
defaultMessage: 'Hide',
17+
}),
18+
description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', {
19+
defaultMessage: 'Do not fill gaps',
20+
}),
21+
},
22+
{
23+
id: 'Zero',
24+
title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', {
25+
defaultMessage: 'Zero',
26+
}),
27+
description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', {
28+
defaultMessage: 'Fill gaps with zeros',
29+
}),
30+
},
31+
{
32+
id: 'Linear',
33+
title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', {
34+
defaultMessage: 'Linear',
35+
}),
36+
description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', {
37+
defaultMessage: 'Fill gaps with a line',
38+
}),
39+
},
40+
{
41+
id: 'Carry',
42+
title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', {
43+
defaultMessage: 'Last',
44+
}),
45+
description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', {
46+
defaultMessage: 'Fill gaps with the last value',
47+
}),
48+
},
49+
{
50+
id: 'Lookahead',
51+
title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', {
52+
defaultMessage: 'Next',
53+
}),
54+
description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', {
55+
defaultMessage: 'Fill gaps with the next value',
56+
}),
57+
},
58+
] as const;
59+
60+
export function getFitEnum(fittingFunction?: FittingFunction) {
61+
if (fittingFunction) {
62+
return Fit[fittingFunction];
63+
}
64+
return Fit.None;
65+
}
66+
67+
export function getFitOptions(fittingFunction?: FittingFunction) {
68+
return { type: getFitEnum(fittingFunction) };
69+
}

x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('#toExpression', () => {
4040
{
4141
legend: { position: Position.Bottom, isVisible: true },
4242
preferredSeriesType: 'bar',
43+
fittingFunction: 'Carry',
4344
layers: [
4445
{
4546
layerId: 'first',
@@ -55,6 +56,27 @@ describe('#toExpression', () => {
5556
).toMatchSnapshot();
5657
});
5758

59+
it('should default the fitting function to None', () => {
60+
expect(
61+
(xyVisualization.toExpression(
62+
{
63+
legend: { position: Position.Bottom, isVisible: true },
64+
preferredSeriesType: 'bar',
65+
layers: [
66+
{
67+
layerId: 'first',
68+
seriesType: 'area',
69+
splitAccessor: 'd',
70+
xAccessor: 'a',
71+
accessors: ['b', 'c'],
72+
},
73+
],
74+
},
75+
frame
76+
) as Ast).chain[0].arguments.fittingFunction[0]
77+
).toEqual('None');
78+
});
79+
5880
it('should not generate an expression when missing x', () => {
5981
expect(
6082
xyVisualization.toExpression(

x-pack/plugins/lens/public/xy_visualization/to_expression.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export const buildExpression = (
133133
],
134134
},
135135
],
136+
fittingFunction: [state.fittingFunction || 'None'],
136137
layers: validLayers.map((layer) => {
137138
const columnToLabel: Record<string, string> = {};
138139

x-pack/plugins/lens/public/xy_visualization/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import chartBarHorizontalStackedSVG from '../assets/chart_bar_horizontal_stacked
1616
import chartLineSVG from '../assets/chart_line.svg';
1717

1818
import { VisualizationType } from '../index';
19+
import { FittingFunction } from './fitting_functions';
1920

2021
export interface LegendConfig {
2122
isVisible: boolean;
@@ -225,12 +226,14 @@ export interface XYArgs {
225226
yTitle: string;
226227
legend: LegendConfig & { type: 'lens_xy_legendConfig' };
227228
layers: LayerArgs[];
229+
fittingFunction?: FittingFunction;
228230
}
229231

230232
// Persisted parts of the state
231233
export interface XYState {
232234
preferredSeriesType: SeriesType;
233235
legend: LegendConfig;
236+
fittingFunction?: FittingFunction;
234237
layers: LayerConfig[];
235238
}
236239

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.lnsXyToolbar__popover {
2+
width: 400px;
3+
}

x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
*/
66

77
import React from 'react';
8-
import { mountWithIntl as mount } from 'test_utils/enzyme_helpers';
9-
import { EuiButtonGroupProps } from '@elastic/eui';
10-
import { LayerContextMenu } from './xy_config_panel';
8+
import { mountWithIntl as mount, shallowWithIntl as shallow } from 'test_utils/enzyme_helpers';
9+
import { EuiButtonGroupProps, EuiSuperSelect } from '@elastic/eui';
10+
import { LayerContextMenu, XyToolbar } from './xy_config_panel';
1111
import { FramePublicAPI } from '../types';
1212
import { State } from './types';
1313
import { Position } from '@elastic/charts';
1414
import { createMockFramePublicAPI, createMockDatasource } from '../editor_frame_service/mocks';
1515

16-
describe('LayerContextMenu', () => {
16+
describe('XY Config panels', () => {
1717
let frame: FramePublicAPI;
1818

1919
function testState(): State {
@@ -39,11 +39,6 @@ describe('LayerContextMenu', () => {
3939
};
4040
});
4141

42-
test.skip('allows toggling of legend visibility', () => {});
43-
test.skip('allows changing legend position', () => {});
44-
test.skip('allows toggling the y axis gridlines', () => {});
45-
test.skip('allows toggling the x axis gridlines', () => {});
46-
4742
describe('LayerContextMenu', () => {
4843
test('enables stacked chart types even when there is no split series', () => {
4944
const state = testState();
@@ -92,4 +87,45 @@ describe('LayerContextMenu', () => {
9287
expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]);
9388
});
9489
});
90+
91+
describe('XyToolbar', () => {
92+
it('should show currently selected fitting function', () => {
93+
const state = testState();
94+
95+
const component = shallow(
96+
<XyToolbar
97+
frame={frame}
98+
setState={jest.fn()}
99+
state={{
100+
...state,
101+
layers: [{ ...state.layers[0], seriesType: 'line' }],
102+
fittingFunction: 'Carry',
103+
}}
104+
/>
105+
);
106+
107+
expect(component.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('Carry');
108+
});
109+
110+
it('should disable the select if there is no unstacked area or line series', () => {
111+
const state = testState();
112+
113+
const component = shallow(
114+
<XyToolbar
115+
frame={frame}
116+
setState={jest.fn()}
117+
state={{
118+
...state,
119+
layers: [
120+
{ ...state.layers[0], seriesType: 'bar' },
121+
{ ...state.layers[0], seriesType: 'area_stacked' },
122+
],
123+
fittingFunction: 'Carry',
124+
}}
125+
/>
126+
);
127+
128+
expect(component.find(EuiSuperSelect).prop('disabled')).toEqual(true);
129+
});
130+
});
95131
});

0 commit comments

Comments
 (0)