Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens] Thresholds added #108342

Merged
merged 63 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f63b5d8
:sparkles: Add threshold code
dej611 Aug 9, 2021
300ab45
fix dropping the field
mbondyra Aug 11, 2021
c22b14d
fix adding bottom axis
mbondyra Aug 11, 2021
5c85dcf
fix do not show horizontal axis for terms and filters
mbondyra Aug 11, 2021
bc1cf5d
extract StaticHeader component
mbondyra Aug 12, 2021
32c1a57
convert section to header
mbondyra Aug 12, 2021
2423cdc
initialize percentage with percentage value
mbondyra Aug 12, 2021
e3a469c
fix debouncing for threshold value and thickness line range, divide h…
mbondyra Aug 12, 2021
5c5ecb5
fix two layer threshold to pie (tbc)
mbondyra Aug 13, 2021
f919159
:white_check_mark: Add first batch of tests
dej611 Aug 25, 2021
2d0f356
:white_check_mark: Added basic tests for layer header
dej611 Aug 25, 2021
a930b43
:white_check_mark: Add editor tabs tests
dej611 Aug 25, 2021
f272020
:white_check_mark: Add more tests
dej611 Aug 25, 2021
d95d441
:white_check_mark: Add indexpattern tests
dej611 Aug 25, 2021
c3d00a0
:white_check_mark: Add operation tests
dej611 Aug 26, 2021
53d85f2
:white_check_mark: Add xy visualization test for thresholds
dej611 Aug 26, 2021
ae4201d
:white_check_mark: Add functional tests for thresholds
dej611 Aug 30, 2021
ead4e50
:white_check_mark: Add one more tests for terms op
dej611 Aug 30, 2021
40069fe
Merge pull request #5 from dej611/lens/thresholds-tests
mbondyra Sep 2, 2021
e49a928
Merge branch 'master' into lens/thresholds
dej611 Sep 2, 2021
d3e0c0f
Merge branch 'master' into lens/thresholds
kibanamachine Sep 2, 2021
1c95f6c
:bug: Fix broken test
dej611 Sep 3, 2021
869889d
:sparkles: Pickup static value when transitioning to formula
dej611 Sep 3, 2021
ac83c8f
Merge branch 'master' into lens/thresholds
kibanamachine Sep 6, 2021
6ad821d
:bug: Fix state update race conditions
dej611 Sep 6, 2021
bf6fa66
:bug: Fix transitions to static_value w/o temporary state
dej611 Sep 7, 2021
9357eb1
:white_check_mark: Fix unit tests
dej611 Sep 7, 2021
d1b1b9a
Merge remote-tracking branch 'upstream/master' into lens/thresholds
dej611 Sep 7, 2021
1e94239
:bug: Fix dnd reordering issue
dej611 Sep 7, 2021
6dbd967
:white_check_mark: Fix functional test
dej611 Sep 8, 2021
7011414
:white_check_mark: Add more functional tests
dej611 Sep 8, 2021
16cc5a0
:sparkles: Add support for invalid threshold dimensions
dej611 Sep 8, 2021
a79d0a2
:white_check_mark: Fix tests for new invalid state + add new tests
dej611 Sep 8, 2021
7430e20
:white_check_mark: Fix functional tests
dej611 Sep 8, 2021
7d9ce7d
:white_check_mark: Fix new functional test
dej611 Sep 8, 2021
9cb1a9c
:bug: Fix reording issue on drop duplication
dej611 Sep 8, 2021
321afc3
Merge branch 'master' into lens/thresholds
kibanamachine Sep 8, 2021
c936612
Merge remote-tracking branch 'upstream/master' into lens/thresholds
dej611 Sep 9, 2021
648d327
:white_check_mark: Address functional tests issues
dej611 Sep 9, 2021
4a26908
Apply suggestions from code review
dej611 Sep 9, 2021
3a34261
:bug: Fix last functional test issue
dej611 Sep 9, 2021
cf82e92
:ok_hand: Integrate feedback
dej611 Sep 9, 2021
2a440a9
Merge branch 'lens/thresholds' of https://github.com/mbondyra/kibana …
dej611 Sep 9, 2021
f81101f
:white_check_mark: Fix test after dnd fix
dej611 Sep 10, 2021
7ffd4cc
:globe_with_meridians: Add i18n to icon labels
dej611 Sep 10, 2021
8b15bb0
Merge branch 'master' into lens/thresholds
kibanamachine Sep 12, 2021
97b0c2b
:recycle: Refactor dimension editor code
dej611 Sep 13, 2021
6ac9a6b
:recycle: Refactor threshold chart code
dej611 Sep 13, 2021
c3e607f
Merge branch 'lens/thresholds' of https://github.com/mbondyra/kibana …
dej611 Sep 13, 2021
c9241c2
:ok_hand: Reduced line width to 10px
dej611 Sep 13, 2021
61115b2
Merge remote-tracking branch 'upstream/master' into lens/thresholds
dej611 Sep 14, 2021
244ec0e
:bug: Fix temporary state when going to quick functions from static v…
dej611 Sep 14, 2021
59c75c0
:bug: Remove format selection in thresholds
dej611 Sep 14, 2021
238d638
:bug: fix axis logic issue + inherit formatter from chart axis
dej611 Sep 15, 2021
b0c23ed
:bug: Do not show thresholds when not required + fix static value com…
dej611 Sep 15, 2021
8a17fe4
Merge remote-tracking branch 'upstream/master' into lens/thresholds
dej611 Sep 15, 2021
55a2555
:bug: Fix line thinkness slider in bound issue
dej611 Sep 15, 2021
89e4981
:lipstick: Change color picker label and tooltip behaviour
dej611 Sep 16, 2021
6c37c69
:bug: Fix static value computation for mixed axis types
dej611 Sep 16, 2021
a430f71
:bug: Fix issue with static value computation covering all edge cases
dej611 Sep 16, 2021
ec4dfef
Merge remote-tracking branch 'upstream/master' into lens/thresholds
dej611 Sep 20, 2021
81fa3bd
:rotating_light: fix linting issue
dej611 Sep 20, 2021
69768de
:bug: Fix debouncing sync issue in panel
dej611 Sep 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ interface AxisConfig {
hide?: boolean;
}

export type YAxisMode = 'auto' | 'left' | 'right';
export type YAxisMode = 'auto' | 'left' | 'right' | 'bottom';
export type LineStyle = 'solid' | 'dashed' | 'dotted';
export type FillStyle = 'none' | 'above' | 'below';

export interface YConfig {
forAccessor: string;
axisMode?: YAxisMode;
color?: string;
icon?: string;
lineWidth?: number;
lineStyle?: LineStyle;
fill?: FillStyle;
}

export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & {
Expand Down Expand Up @@ -161,6 +167,24 @@ export const yAxisConfig: ExpressionFunctionDefinition<
types: ['string'],
help: 'The color of the series',
},
lineStyle: {
types: ['string'],
options: ['solid', 'dotted', 'dashed'],
help: 'The style of the threshold line',
},
lineWidth: {
types: ['number'],
help: 'The width of the threshold line',
},
icon: {
types: ['string'],
help: 'An optional icon used for threshold lines',
},
fill: {
types: ['string'],
options: ['none', 'above', 'below'],
help: '',
},
},
fn: function fn(input: unknown, args: YConfig) {
return {
Expand Down
40 changes: 40 additions & 0 deletions x-pack/plugins/lens/public/assets/chart_bar_threshold.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 React from 'react';
import { EuiIconProps } from '@elastic/eui';

export const LensIconChartBarThreshold = ({
title,
titleId,
...props
}: Omit<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 16 12"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<g>
<path
className="lensChartIcon__subdued"
d="M3.2 4.79997C3.2 4.50542 2.96122 4.26663 2.66667 4.26663H0.533333C0.238784 4.26663 0 4.50542 0 4.79997V6.39997H3.2V4.79997ZM3.2 9.59997H0V13.3333C0 13.6279 0.238784 13.8666 0.533333 13.8666H2.66667C2.96122 13.8666 3.2 13.6279 3.2 13.3333V9.59997ZM8.53333 9.59997H11.7333V13.3333C11.7333 13.6279 11.4946 13.8666 11.2 13.8666H9.06667C8.77211 13.8666 8.53333 13.6279 8.53333 13.3333V9.59997ZM11.7333 6.39997H8.53333V2.66663C8.53333 2.37208 8.77211 2.1333 9.06667 2.1333H11.2C11.4946 2.1333 11.7333 2.37208 11.7333 2.66663V6.39997ZM12.8 9.59997V13.3333C12.8 13.6279 13.0388 13.8666 13.3333 13.8666H15.4667C15.7612 13.8666 16 13.6279 16 13.3333V9.59997H12.8ZM16 6.39997V5.86663C16 5.57208 15.7612 5.3333 15.4667 5.3333H13.3333C13.0388 5.3333 12.8 5.57208 12.8 5.86663V6.39997H16ZM7.46667 11.2C7.46667 10.9054 7.22789 10.6666 6.93333 10.6666H4.8C4.50544 10.6666 4.26667 10.9054 4.26667 11.2V13.3333C4.26667 13.6279 4.50544 13.8666 4.8 13.8666H6.93333C7.22789 13.8666 7.46667 13.6279 7.46667 13.3333V11.2Z"
/>
<rect
y="7.4668"
width="16"
height="1.06667"
rx="0.533334"
className="lensChartIcon__accent"
/>
</g>
</svg>
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import {
EuiToolTip,
EuiButton,
Expand Down Expand Up @@ -38,12 +38,17 @@ export function AddLayerButton({
}: AddLayerButtonProps) {
const [showLayersChoice, toggleLayersChoice] = useState(false);

const hasMultipleLayers = Boolean(visualization.appendLayer && visualizationState);
if (!hasMultipleLayers) {
const supportedLayers = useMemo(() => {
if (!visualization.appendLayer || !visualizationState) {
return null;
}
return visualization.getSupportedLayers?.(visualizationState, layersMeta);
}, [visualization, visualizationState, layersMeta]);

if (supportedLayers == null) {
return null;
}
const supportedLayers = visualization.getSupportedLayers?.(visualizationState, layersMeta);
if (supportedLayers?.length === 1) {
if (supportedLayers.length === 1) {
return (
<EuiToolTip
display="block"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ import { LayerPanel } from './layer_panel';
import { coreMock } from 'src/core/public/mocks';
import { generateId } from '../../../id_generator';
import { mountWithProvider } from '../../../mocks';
import { layerTypes } from '../../../../common';
import { ReactWrapper } from 'enzyme';

jest.mock('../../../id_generator');

const waitMs = (time: number) => new Promise((r) => setTimeout(r, time));

let container: HTMLDivElement | undefined;

beforeEach(() => {
Expand Down Expand Up @@ -137,7 +141,7 @@ describe('ConfigPanel', () => {

const updater = () => 'updated';
updateDatasource('mockindexpattern', updater);
await new Promise((r) => setTimeout(r, 0));
await waitMs(0);
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
expect(
(lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater(
Expand All @@ -147,7 +151,7 @@ describe('ConfigPanel', () => {

updateAll('mockindexpattern', updater, props.visualizationState);
// wait for one tick so async updater has a chance to trigger
await new Promise((r) => setTimeout(r, 0));
await waitMs(0);
expect(lensStore.dispatch).toHaveBeenCalledTimes(2);
expect(
(lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater(
Expand Down Expand Up @@ -293,4 +297,164 @@ describe('ConfigPanel', () => {
expect(focusedEl?.children[0].getAttribute('data-test-subj')).toEqual('lns-layerPanel-1');
});
});

describe('initial default value', () => {
function prepareAndMountComponent(props: ReturnType<typeof getDefaultProps>) {
(generateId as jest.Mock).mockReturnValue(`newId`);
return mountWithProvider(
<LayerPanels {...props} />,

{
preloadedState: {
datasourceStates: {
mockindexpattern: {
isLoading: false,
state: 'state',
},
},
activeDatasourceId: 'mockindexpattern',
},
},
{
attachTo: container,
}
);
}
function clickToAddLayer(instance: ReactWrapper) {
act(() => {
instance.find('[data-test-subj="lnsLayerAddButton"]').first().simulate('click');
});
instance.update();
act(() => {
instance
.find(`[data-test-subj="lnsLayerAddButton-${layerTypes.THRESHOLD}"]`)
.first()
.simulate('click');
});
instance.update();

return waitMs(0);
}

function clickToAddDimension(instance: ReactWrapper) {
act(() => {
instance.find('[data-test-subj="lns-empty-dimension"]').last().simulate('click');
});
return waitMs(0);
}

it('should not add an initial dimension when not specified', async () => {
const props = getDefaultProps();
props.activeVisualization.getSupportedLayers = jest.fn(() => [
{ type: layerTypes.DATA, label: 'Data Layer' },
{
type: layerTypes.THRESHOLD,
label: 'Threshold layer',
},
]);
mockDatasource.initializeDimension = jest.fn();

const { instance, lensStore } = await prepareAndMountComponent(props);
await clickToAddLayer(instance);

expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
expect(mockDatasource.initializeDimension).not.toHaveBeenCalled();
});

it('should not add an initial dimension when initialDimensions are not available for the given layer type', async () => {
const props = getDefaultProps();
props.activeVisualization.getSupportedLayers = jest.fn(() => [
{
type: layerTypes.DATA,
label: 'Data Layer',
initialDimensions: [
{
groupId: 'testGroup',
columnId: 'myColumn',
dataType: 'number',
label: 'Initial value',
staticValue: 100,
},
],
},
{
type: layerTypes.THRESHOLD,
label: 'Threshold layer',
},
]);
mockDatasource.initializeDimension = jest.fn();

const { instance, lensStore } = await prepareAndMountComponent(props);
await clickToAddLayer(instance);

expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
expect(mockDatasource.initializeDimension).not.toHaveBeenCalled();
});

it('should use group initial dimension value when adding a new layer if available', async () => {
const props = getDefaultProps();
props.activeVisualization.getSupportedLayers = jest.fn(() => [
{ type: layerTypes.DATA, label: 'Data Layer' },
{
type: layerTypes.THRESHOLD,
label: 'Threshold layer',
initialDimensions: [
{
groupId: 'testGroup',
columnId: 'myColumn',
dataType: 'number',
label: 'Initial value',
staticValue: 100,
},
],
},
]);
mockDatasource.initializeDimension = jest.fn();

const { instance, lensStore } = await prepareAndMountComponent(props);
await clickToAddLayer(instance);

expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
expect(mockDatasource.initializeDimension).toHaveBeenCalledWith(undefined, 'newId', {
columnId: 'myColumn',
dataType: 'number',
groupId: 'testGroup',
label: 'Initial value',
staticValue: 100,
});
});

it('should add an initial dimension value when clicking on the empty dimension button', async () => {
const props = getDefaultProps();
props.activeVisualization.getSupportedLayers = jest.fn(() => [
{
type: layerTypes.DATA,
label: 'Data Layer',
initialDimensions: [
{
groupId: 'a',
columnId: 'newId',
dataType: 'number',
label: 'Initial value',
staticValue: 100,
},
],
},
]);
mockDatasource.initializeDimension = jest.fn();

const { instance, lensStore } = await prepareAndMountComponent(props);

await clickToAddDimension(instance);
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);

expect(mockDatasource.initializeDimension).toHaveBeenCalledWith('state', 'first', {
groupId: 'a',
columnId: 'newId',
dataType: 'number',
label: 'Initial value',
staticValue: 100,
});
});
});
});
Loading