Skip to content

Commit 8656337

Browse files
authored
[7.x] Create vis_type_xy plugin to replace histogram, area and line charts (#78154) (#86534)
1 parent 1bcb2d4 commit 8656337

File tree

282 files changed

+8364
-2763
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

282 files changed

+8364
-2763
lines changed

docs/developer/plugin-list.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ heatmap charts.
268268
269269
|{kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy]
270270
|Contains the new xy-axis chart using the elastic-charts library, which will eventually
271-
replace the vislib xy-axis (bar, area, line) charts.
271+
replace the vislib xy-axis charts including bar, area, and line.
272272
273273
274274
|{kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations]

docs/management/advanced-options.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ of buckets to try to represent.
454454
==== Visualization
455455

456456
[horizontal]
457+
[[visualization-visualize-chartslibrary]]`visualization:visualize:chartsLibrary`::
458+
Enables the new charts library for area, line, and bar charts in visualization panels. Does *not* support the split chart aggregation.
459+
457460
[[visualization-colormapping]]`visualization:colorMapping`::
458461
**This setting is deprecated and will not be supported as of 8.0.**
459462
Maps values to specific colors in *Visualize* charts and *TSVB*. This setting does not apply to *Lens*.

packages/kbn-optimizer/limits.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pageLoadAssetSize:
66
beatsManagement: 188135
77
bfetch: 41874
88
canvas: 1065624
9-
charts: 159211
9+
charts: 195358
1010
cloud: 21076
1111
console: 46235
1212
core: 692684
@@ -98,7 +98,7 @@ pageLoadAssetSize:
9898
visTypeTimeseries: 155347
9999
visTypeVega: 153861
100100
visTypeVislib: 242982
101-
visTypeXy: 20255
101+
visTypeXy: 113478
102102
visualizations: 295169
103103
visualize: 57433
104104
watcher: 43742

src/plugins/charts/public/services/legacy_colors/colors.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@ describe('Vislib Color Service', () => {
6262

6363
it('should throw an error if input is not an array', () => {
6464
expect(() => {
65+
// @ts-expect-error
6566
colors.createColorLookupFunction(200);
6667
}).toThrowError();
6768

6869
expect(() => {
70+
// @ts-expect-error
6971
colors.createColorLookupFunction('help');
7072
}).toThrowError();
7173

7274
expect(() => {
75+
// @ts-expect-error
7376
colors.createColorLookupFunction(true);
7477
}).toThrowError();
7578

@@ -78,25 +81,30 @@ describe('Vislib Color Service', () => {
7881
}).toThrowError();
7982

8083
expect(() => {
84+
// @ts-expect-error
8185
colors.createColorLookupFunction(nullValue);
8286
}).toThrowError();
8387

8488
expect(() => {
89+
// @ts-expect-error
8590
colors.createColorLookupFunction(emptyObject);
8691
}).toThrowError();
8792
});
8893

8994
describe('when array is not composed of numbers, strings, or undefined values', () => {
9095
it('should throw an error', () => {
9196
expect(() => {
97+
// @ts-expect-error
9298
colors.createColorLookupFunction(arrayOfObjects);
9399
}).toThrowError();
94100

95101
expect(() => {
102+
// @ts-expect-error
96103
colors.createColorLookupFunction(arrayOfBooleans);
97104
}).toThrowError();
98105

99106
expect(() => {
107+
// @ts-expect-error
100108
colors.createColorLookupFunction(arrayOfNullValues);
101109
}).toThrowError();
102110
});
@@ -113,6 +121,7 @@ describe('Vislib Color Service', () => {
113121
}).not.toThrowError();
114122

115123
expect(() => {
124+
// @ts-expect-error
116125
colors.createColorLookupFunction(arrayOfUndefinedValues);
117126
}).not.toThrowError();
118127
});

src/plugins/charts/public/services/legacy_colors/colors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class LegacyColorsService {
4848
}
4949

5050
createColorLookupFunction(
51-
arrayOfStringsOrNumbers?: any,
51+
arrayOfStringsOrNumbers?: Array<string | number>,
5252
colorMapping: Partial<Record<string, string>> = {}
5353
) {
5454
if (!Array.isArray(arrayOfStringsOrNumbers)) {
@@ -67,7 +67,7 @@ export class LegacyColorsService {
6767

6868
this.mappedColors.mapKeys(arrayOfStringsOrNumbers);
6969

70-
return (value: string) => {
70+
return (value: string | number) => {
7171
return colorMapping[value] || this.mappedColors.get(value);
7272
};
7373
}

src/plugins/charts/public/static/components/basic_options.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
*/
1919

2020
import React from 'react';
21+
2122
import { i18n } from '@kbn/i18n';
2223

23-
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
24+
import { VisOptionsProps } from '../../../../vis_default_editor/public';
25+
2426
import { SwitchOption } from './switch';
2527
import { SelectOption } from './select';
2628

src/plugins/charts/public/static/components/collections.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@
1818
*/
1919

2020
import { $Values } from '@kbn/utility-types';
21+
import { i18n } from '@kbn/i18n';
2122

22-
export const ColorModes = Object.freeze({
23-
BACKGROUND: 'Background' as 'Background',
24-
LABELS: 'Labels' as 'Labels',
25-
NONE: 'None' as 'None',
23+
export const ColorMode = Object.freeze({
24+
Background: 'Background' as 'Background',
25+
Labels: 'Labels' as 'Labels',
26+
None: 'None' as 'None',
2627
});
27-
export type ColorModes = $Values<typeof ColorModes>;
28+
export type ColorMode = $Values<typeof ColorMode>;
2829

29-
export const Rotates = Object.freeze({
30-
HORIZONTAL: 0,
31-
VERTICAL: 90,
32-
ANGLED: 75,
30+
export const LabelRotation = Object.freeze({
31+
Horizontal: 0,
32+
Vertical: 90,
33+
Angled: 75,
34+
});
35+
export type LabelRotation = $Values<typeof LabelRotation>;
36+
37+
export const defaultCountLabel = i18n.translate('charts.countText', {
38+
defaultMessage: 'Count',
3339
});
34-
export type Rotates = $Values<typeof Rotates>;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
$visColorPickerWidth: $euiSizeL * 8; // 8 columns
2+
3+
.visColorPicker__value {
4+
width: $visColorPickerWidth;
5+
}
6+
7+
.visColorPicker__valueDot {
8+
cursor: pointer;
9+
10+
&:hover {
11+
transform: scale(1.4);
12+
}
13+
14+
&-isSelected {
15+
border: $euiSizeXS solid;
16+
border-radius: 100%;
17+
}
18+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import classNames from 'classnames';
21+
import React, { BaseSyntheticEvent } from 'react';
22+
23+
import { EuiButtonEmpty, EuiFlexItem, EuiIcon } from '@elastic/eui';
24+
import { FormattedMessage } from '@kbn/i18n/react';
25+
26+
import './color_picker.scss';
27+
28+
export const legendColors: string[] = [
29+
'#3F6833',
30+
'#967302',
31+
'#2F575E',
32+
'#99440A',
33+
'#58140C',
34+
'#052B51',
35+
'#511749',
36+
'#3F2B5B',
37+
'#508642',
38+
'#CCA300',
39+
'#447EBC',
40+
'#C15C17',
41+
'#890F02',
42+
'#0A437C',
43+
'#6D1F62',
44+
'#584477',
45+
'#629E51',
46+
'#E5AC0E',
47+
'#64B0C8',
48+
'#E0752D',
49+
'#BF1B00',
50+
'#0A50A1',
51+
'#962D82',
52+
'#614D93',
53+
'#7EB26D',
54+
'#EAB839',
55+
'#6ED0E0',
56+
'#EF843C',
57+
'#E24D42',
58+
'#1F78C1',
59+
'#BA43A9',
60+
'#705DA0',
61+
'#9AC48A',
62+
'#F2C96D',
63+
'#65C5DB',
64+
'#F9934E',
65+
'#EA6460',
66+
'#5195CE',
67+
'#D683CE',
68+
'#806EB7',
69+
'#B7DBAB',
70+
'#F4D598',
71+
'#70DBED',
72+
'#F9BA8F',
73+
'#F29191',
74+
'#82B5D8',
75+
'#E5A8E2',
76+
'#AEA2E0',
77+
'#E0F9D7',
78+
'#FCEACA',
79+
'#CFFAFF',
80+
'#F9E2D2',
81+
'#FCE2DE',
82+
'#BADFF4',
83+
'#F9D9F9',
84+
'#DEDAF7',
85+
];
86+
87+
interface ColorPickerProps {
88+
id?: string;
89+
label: string | number | null;
90+
onChange: (color: string | null, event: BaseSyntheticEvent) => void;
91+
color: string;
92+
}
93+
94+
export const ColorPicker = ({ onChange, color: selectedColor, id, label }: ColorPickerProps) => (
95+
<div className="visColorPicker">
96+
<span id={`${id}ColorPickerDesc`} className="euiScreenReaderOnly">
97+
<FormattedMessage
98+
id="charts.colorPicker.setColor.screenReaderDescription"
99+
defaultMessage="Set color for value {legendDataLabel}"
100+
values={{ legendDataLabel: label }}
101+
/>
102+
</span>
103+
<div className="visColorPicker__value" role="listbox">
104+
{legendColors.map((color) => (
105+
<EuiIcon
106+
role="option"
107+
tabIndex={0}
108+
type="dot"
109+
size="l"
110+
color={selectedColor}
111+
key={color}
112+
aria-label={color}
113+
aria-describedby={`${id}ColorPickerDesc`}
114+
aria-selected={color === selectedColor}
115+
onClick={(e) => onChange(color, e)}
116+
onKeyPress={(e) => onChange(color, e)}
117+
className={classNames('visColorPicker__valueDot', {
118+
// eslint-disable-next-line @typescript-eslint/naming-convention
119+
'visColorPicker__valueDot-isSelected': color === selectedColor,
120+
})}
121+
style={{ color }}
122+
data-test-subj={`visColorPickerColor-${color}`}
123+
/>
124+
))}
125+
</div>
126+
{legendColors.some((c) => c === selectedColor) && (
127+
<EuiFlexItem grow={false}>
128+
<EuiButtonEmpty
129+
size="s"
130+
onClick={(e: any) => onChange(null, e)}
131+
onKeyPress={(e: any) => onChange(null, e)}
132+
>
133+
<FormattedMessage id="charts.colorPicker.clearColor" defaultMessage="Clear color" />
134+
</EuiButtonEmpty>
135+
</EuiFlexItem>
136+
)}
137+
</div>
138+
);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import moment, { Moment } from 'moment';
20+
import React, { FC } from 'react';
21+
22+
import { LineAnnotation, AnnotationDomainTypes, LineAnnotationStyle } from '@elastic/charts';
23+
import lightEuiTheme from '@elastic/eui/dist/eui_theme_light.json';
24+
import darkEuiTheme from '@elastic/eui/dist/eui_theme_dark.json';
25+
26+
interface CurrentTimeProps {
27+
isDarkMode: boolean;
28+
domainEnd?: number | Moment;
29+
}
30+
31+
/**
32+
* Render current time line annotation on @elastic/charts `Chart`
33+
*/
34+
export const CurrentTime: FC<CurrentTimeProps> = ({ isDarkMode, domainEnd }) => {
35+
const lineAnnotationStyle: Partial<LineAnnotationStyle> = {
36+
line: {
37+
strokeWidth: 2,
38+
stroke: isDarkMode ? darkEuiTheme.euiColorDanger : lightEuiTheme.euiColorDanger,
39+
opacity: 0.7,
40+
},
41+
};
42+
43+
// Domain end of 'now' will be milliseconds behind current time, so we extend time by 1 minute and check if
44+
// the annotation is within this range; if so, the line annotation uses the domainEnd as its value
45+
const now = moment();
46+
const isAnnotationAtEdge = domainEnd
47+
? moment(domainEnd).add(1, 'm').isAfter(now) && now.isAfter(domainEnd)
48+
: false;
49+
const lineAnnotationData = [
50+
{
51+
dataValue: isAnnotationAtEdge ? domainEnd : now.valueOf(),
52+
},
53+
];
54+
55+
return (
56+
<LineAnnotation
57+
id="__current-time__"
58+
hideTooltips
59+
domainType={AnnotationDomainTypes.XDomain}
60+
dataValues={lineAnnotationData}
61+
style={lineAnnotationStyle}
62+
/>
63+
);
64+
};

0 commit comments

Comments
 (0)