Create vis_type_xy plugin to replace histogram, area and line charts (#78154)
This commit is contained in:
parent
cea865f5a9
commit
ddea10e718
|
@ -272,7 +272,7 @@ heatmap charts.
|
||||||
|
|
||||||
|{kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy]
|
|{kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy]
|
||||||
|Contains the new xy-axis chart using the elastic-charts library, which will eventually
|
|Contains the new xy-axis chart using the elastic-charts library, which will eventually
|
||||||
replace the vislib xy-axis (bar, area, line) charts.
|
replace the vislib xy-axis charts including bar, area, and line.
|
||||||
|
|
||||||
|
|
||||||
|{kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations]
|
|{kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations]
|
||||||
|
|
|
@ -453,6 +453,9 @@ of buckets to try to represent.
|
||||||
==== Visualization
|
==== Visualization
|
||||||
|
|
||||||
[horizontal]
|
[horizontal]
|
||||||
|
[[visualization-visualize-chartslibrary]]`visualization:visualize:chartsLibrary`::
|
||||||
|
Enables the new charts library for area, line, and bar charts in visualization panels. Does *not* support the split chart aggregation.
|
||||||
|
|
||||||
[[visualization-colormapping]]`visualization:colorMapping`::
|
[[visualization-colormapping]]`visualization:colorMapping`::
|
||||||
**This setting is deprecated and will not be supported as of 8.0.**
|
**This setting is deprecated and will not be supported as of 8.0.**
|
||||||
Maps values to specific colors in *Visualize* charts and *TSVB*. This setting does not apply to *Lens*.
|
Maps values to specific colors in *Visualize* charts and *TSVB*. This setting does not apply to *Lens*.
|
||||||
|
|
|
@ -6,7 +6,7 @@ pageLoadAssetSize:
|
||||||
beatsManagement: 188135
|
beatsManagement: 188135
|
||||||
bfetch: 41874
|
bfetch: 41874
|
||||||
canvas: 1066647
|
canvas: 1066647
|
||||||
charts: 159211
|
charts: 195358
|
||||||
cloud: 21076
|
cloud: 21076
|
||||||
console: 46091
|
console: 46091
|
||||||
core: 692106
|
core: 692106
|
||||||
|
@ -98,7 +98,7 @@ pageLoadAssetSize:
|
||||||
visTypeTimeseries: 155203
|
visTypeTimeseries: 155203
|
||||||
visTypeVega: 153573
|
visTypeVega: 153573
|
||||||
visTypeVislib: 242838
|
visTypeVislib: 242838
|
||||||
visTypeXy: 20255
|
visTypeXy: 113478
|
||||||
visualizations: 295025
|
visualizations: 295025
|
||||||
visualize: 57431
|
visualize: 57431
|
||||||
watcher: 43598
|
watcher: 43598
|
||||||
|
|
|
@ -62,14 +62,17 @@ describe('Vislib Color Service', () => {
|
||||||
|
|
||||||
it('should throw an error if input is not an array', () => {
|
it('should throw an error if input is not an array', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(200);
|
colors.createColorLookupFunction(200);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction('help');
|
colors.createColorLookupFunction('help');
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(true);
|
colors.createColorLookupFunction(true);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
|
@ -78,10 +81,12 @@ describe('Vislib Color Service', () => {
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(nullValue);
|
colors.createColorLookupFunction(nullValue);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(emptyObject);
|
colors.createColorLookupFunction(emptyObject);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
});
|
});
|
||||||
|
@ -89,14 +94,17 @@ describe('Vislib Color Service', () => {
|
||||||
describe('when array is not composed of numbers, strings, or undefined values', () => {
|
describe('when array is not composed of numbers, strings, or undefined values', () => {
|
||||||
it('should throw an error', () => {
|
it('should throw an error', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(arrayOfObjects);
|
colors.createColorLookupFunction(arrayOfObjects);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(arrayOfBooleans);
|
colors.createColorLookupFunction(arrayOfBooleans);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(arrayOfNullValues);
|
colors.createColorLookupFunction(arrayOfNullValues);
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
});
|
});
|
||||||
|
@ -113,6 +121,7 @@ describe('Vislib Color Service', () => {
|
||||||
}).not.toThrowError();
|
}).not.toThrowError();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
// @ts-expect-error
|
||||||
colors.createColorLookupFunction(arrayOfUndefinedValues);
|
colors.createColorLookupFunction(arrayOfUndefinedValues);
|
||||||
}).not.toThrowError();
|
}).not.toThrowError();
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class LegacyColorsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
createColorLookupFunction(
|
createColorLookupFunction(
|
||||||
arrayOfStringsOrNumbers?: any,
|
arrayOfStringsOrNumbers?: Array<string | number>,
|
||||||
colorMapping: Partial<Record<string, string>> = {}
|
colorMapping: Partial<Record<string, string>> = {}
|
||||||
) {
|
) {
|
||||||
if (!Array.isArray(arrayOfStringsOrNumbers)) {
|
if (!Array.isArray(arrayOfStringsOrNumbers)) {
|
||||||
|
@ -67,7 +67,7 @@ export class LegacyColorsService {
|
||||||
|
|
||||||
this.mappedColors.mapKeys(arrayOfStringsOrNumbers);
|
this.mappedColors.mapKeys(arrayOfStringsOrNumbers);
|
||||||
|
|
||||||
return (value: string) => {
|
return (value: string | number) => {
|
||||||
return colorMapping[value] || this.mappedColors.get(value);
|
return colorMapping[value] || this.mappedColors.get(value);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from '../../../../vis_default_editor/public';
|
||||||
|
|
||||||
import { SwitchOption } from './switch';
|
import { SwitchOption } from './switch';
|
||||||
import { SelectOption } from './select';
|
import { SelectOption } from './select';
|
||||||
|
|
||||||
|
|
|
@ -18,17 +18,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { $Values } from '@kbn/utility-types';
|
import { $Values } from '@kbn/utility-types';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
export const ColorModes = Object.freeze({
|
export const ColorMode = Object.freeze({
|
||||||
BACKGROUND: 'Background' as 'Background',
|
Background: 'Background' as 'Background',
|
||||||
LABELS: 'Labels' as 'Labels',
|
Labels: 'Labels' as 'Labels',
|
||||||
NONE: 'None' as 'None',
|
None: 'None' as 'None',
|
||||||
});
|
});
|
||||||
export type ColorModes = $Values<typeof ColorModes>;
|
export type ColorMode = $Values<typeof ColorMode>;
|
||||||
|
|
||||||
export const Rotates = Object.freeze({
|
export const LabelRotation = Object.freeze({
|
||||||
HORIZONTAL: 0,
|
Horizontal: 0,
|
||||||
VERTICAL: 90,
|
Vertical: 90,
|
||||||
ANGLED: 75,
|
Angled: 75,
|
||||||
|
});
|
||||||
|
export type LabelRotation = $Values<typeof LabelRotation>;
|
||||||
|
|
||||||
|
export const defaultCountLabel = i18n.translate('charts.countText', {
|
||||||
|
defaultMessage: 'Count',
|
||||||
});
|
});
|
||||||
export type Rotates = $Values<typeof Rotates>;
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
$visColorPickerWidth: $euiSizeL * 8; // 8 columns
|
||||||
|
|
||||||
|
.visColorPicker__value {
|
||||||
|
width: $visColorPickerWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visColorPicker__valueDot {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-isSelected {
|
||||||
|
border: $euiSizeXS solid;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
}
|
138
src/plugins/charts/public/static/components/color_picker.tsx
Normal file
138
src/plugins/charts/public/static/components/color_picker.tsx
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { BaseSyntheticEvent } from 'react';
|
||||||
|
|
||||||
|
import { EuiButtonEmpty, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
|
import './color_picker.scss';
|
||||||
|
|
||||||
|
export const legendColors: string[] = [
|
||||||
|
'#3F6833',
|
||||||
|
'#967302',
|
||||||
|
'#2F575E',
|
||||||
|
'#99440A',
|
||||||
|
'#58140C',
|
||||||
|
'#052B51',
|
||||||
|
'#511749',
|
||||||
|
'#3F2B5B',
|
||||||
|
'#508642',
|
||||||
|
'#CCA300',
|
||||||
|
'#447EBC',
|
||||||
|
'#C15C17',
|
||||||
|
'#890F02',
|
||||||
|
'#0A437C',
|
||||||
|
'#6D1F62',
|
||||||
|
'#584477',
|
||||||
|
'#629E51',
|
||||||
|
'#E5AC0E',
|
||||||
|
'#64B0C8',
|
||||||
|
'#E0752D',
|
||||||
|
'#BF1B00',
|
||||||
|
'#0A50A1',
|
||||||
|
'#962D82',
|
||||||
|
'#614D93',
|
||||||
|
'#7EB26D',
|
||||||
|
'#EAB839',
|
||||||
|
'#6ED0E0',
|
||||||
|
'#EF843C',
|
||||||
|
'#E24D42',
|
||||||
|
'#1F78C1',
|
||||||
|
'#BA43A9',
|
||||||
|
'#705DA0',
|
||||||
|
'#9AC48A',
|
||||||
|
'#F2C96D',
|
||||||
|
'#65C5DB',
|
||||||
|
'#F9934E',
|
||||||
|
'#EA6460',
|
||||||
|
'#5195CE',
|
||||||
|
'#D683CE',
|
||||||
|
'#806EB7',
|
||||||
|
'#B7DBAB',
|
||||||
|
'#F4D598',
|
||||||
|
'#70DBED',
|
||||||
|
'#F9BA8F',
|
||||||
|
'#F29191',
|
||||||
|
'#82B5D8',
|
||||||
|
'#E5A8E2',
|
||||||
|
'#AEA2E0',
|
||||||
|
'#E0F9D7',
|
||||||
|
'#FCEACA',
|
||||||
|
'#CFFAFF',
|
||||||
|
'#F9E2D2',
|
||||||
|
'#FCE2DE',
|
||||||
|
'#BADFF4',
|
||||||
|
'#F9D9F9',
|
||||||
|
'#DEDAF7',
|
||||||
|
];
|
||||||
|
|
||||||
|
interface ColorPickerProps {
|
||||||
|
id?: string;
|
||||||
|
label: string | number | null;
|
||||||
|
onChange: (color: string | null, event: BaseSyntheticEvent) => void;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ColorPicker = ({ onChange, color: selectedColor, id, label }: ColorPickerProps) => (
|
||||||
|
<div className="visColorPicker">
|
||||||
|
<span id={`${id}ColorPickerDesc`} className="euiScreenReaderOnly">
|
||||||
|
<FormattedMessage
|
||||||
|
id="charts.colorPicker.setColor.screenReaderDescription"
|
||||||
|
defaultMessage="Set color for value {legendDataLabel}"
|
||||||
|
values={{ legendDataLabel: label }}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div className="visColorPicker__value" role="listbox">
|
||||||
|
{legendColors.map((color) => (
|
||||||
|
<EuiIcon
|
||||||
|
role="option"
|
||||||
|
tabIndex={0}
|
||||||
|
type="dot"
|
||||||
|
size="l"
|
||||||
|
color={selectedColor}
|
||||||
|
key={color}
|
||||||
|
aria-label={color}
|
||||||
|
aria-describedby={`${id}ColorPickerDesc`}
|
||||||
|
aria-selected={color === selectedColor}
|
||||||
|
onClick={(e) => onChange(color, e)}
|
||||||
|
onKeyPress={(e) => onChange(color, e)}
|
||||||
|
className={classNames('visColorPicker__valueDot', {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
'visColorPicker__valueDot-isSelected': color === selectedColor,
|
||||||
|
})}
|
||||||
|
style={{ color }}
|
||||||
|
data-test-subj={`visColorPickerColor-${color}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{legendColors.some((c) => c === selectedColor) && (
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiButtonEmpty
|
||||||
|
size="s"
|
||||||
|
onClick={(e: any) => onChange(null, e)}
|
||||||
|
onKeyPress={(e: any) => onChange(null, e)}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="charts.colorPicker.clearColor" defaultMessage="Clear color" />
|
||||||
|
</EuiButtonEmpty>
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
64
src/plugins/charts/public/static/components/current_time.tsx
Normal file
64
src/plugins/charts/public/static/components/current_time.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import moment, { Moment } from 'moment';
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import { LineAnnotation, AnnotationDomainTypes, LineAnnotationStyle } from '@elastic/charts';
|
||||||
|
import lightEuiTheme from '@elastic/eui/dist/eui_theme_light.json';
|
||||||
|
import darkEuiTheme from '@elastic/eui/dist/eui_theme_dark.json';
|
||||||
|
|
||||||
|
interface CurrentTimeProps {
|
||||||
|
isDarkMode: boolean;
|
||||||
|
domainEnd?: number | Moment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render current time line annotation on @elastic/charts `Chart`
|
||||||
|
*/
|
||||||
|
export const CurrentTime: FC<CurrentTimeProps> = ({ isDarkMode, domainEnd }) => {
|
||||||
|
const lineAnnotationStyle: Partial<LineAnnotationStyle> = {
|
||||||
|
line: {
|
||||||
|
strokeWidth: 2,
|
||||||
|
stroke: isDarkMode ? darkEuiTheme.euiColorDanger : lightEuiTheme.euiColorDanger,
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Domain end of 'now' will be milliseconds behind current time, so we extend time by 1 minute and check if
|
||||||
|
// the annotation is within this range; if so, the line annotation uses the domainEnd as its value
|
||||||
|
const now = moment();
|
||||||
|
const isAnnotationAtEdge = domainEnd
|
||||||
|
? moment(domainEnd).add(1, 'm').isAfter(now) && now.isAfter(domainEnd)
|
||||||
|
: false;
|
||||||
|
const lineAnnotationData = [
|
||||||
|
{
|
||||||
|
dataValue: isAnnotationAtEdge ? domainEnd : now.valueOf(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LineAnnotation
|
||||||
|
id="__current-time__"
|
||||||
|
hideTooltips
|
||||||
|
domainType={AnnotationDomainTypes.XDomain}
|
||||||
|
dataValues={lineAnnotationData}
|
||||||
|
style={lineAnnotationStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
197
src/plugins/charts/public/static/components/endzones.tsx
Normal file
197
src/plugins/charts/public/static/components/endzones.tsx
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import moment, { unitOfTime } from 'moment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TooltipValue,
|
||||||
|
RectAnnotation,
|
||||||
|
RectAnnotationDatum,
|
||||||
|
RectAnnotationStyle,
|
||||||
|
} from '@elastic/charts';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui';
|
||||||
|
import lightEuiTheme from '@elastic/eui/dist/eui_theme_light.json';
|
||||||
|
import darkEuiTheme from '@elastic/eui/dist/eui_theme_dark.json';
|
||||||
|
|
||||||
|
interface EndzonesProps {
|
||||||
|
isDarkMode: boolean;
|
||||||
|
domainStart: number;
|
||||||
|
domainEnd: number;
|
||||||
|
interval: number;
|
||||||
|
domainMin: number;
|
||||||
|
domainMax: number;
|
||||||
|
hideTooltips?: boolean;
|
||||||
|
/**
|
||||||
|
* used to toggle full bin endzones for multiple non-stacked bars
|
||||||
|
*/
|
||||||
|
isFullBin?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Endzones: FC<EndzonesProps> = ({
|
||||||
|
isDarkMode,
|
||||||
|
domainStart,
|
||||||
|
domainEnd,
|
||||||
|
interval,
|
||||||
|
domainMin,
|
||||||
|
domainMax,
|
||||||
|
hideTooltips = true,
|
||||||
|
isFullBin = false,
|
||||||
|
}) => {
|
||||||
|
const rectAnnotationStyle: Partial<RectAnnotationStyle> = {
|
||||||
|
stroke: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade,
|
||||||
|
strokeWidth: 0,
|
||||||
|
opacity: isDarkMode ? 0.6 : 0.2,
|
||||||
|
fill: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rectAnnotations: RectAnnotationDatum[] = [];
|
||||||
|
|
||||||
|
if (domainStart > domainMin) {
|
||||||
|
rectAnnotations.push({
|
||||||
|
coordinates: {
|
||||||
|
x1: isFullBin ? domainMin : domainStart,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domainEnd - interval < domainMax) {
|
||||||
|
rectAnnotations.push({
|
||||||
|
coordinates: {
|
||||||
|
x0: isFullBin ? domainMax : domainEnd,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RectAnnotation
|
||||||
|
id="__endzones__"
|
||||||
|
hideTooltips={hideTooltips}
|
||||||
|
customTooltipDetails={Prompt}
|
||||||
|
zIndex={2}
|
||||||
|
dataValues={rectAnnotations}
|
||||||
|
style={rectAnnotationStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findIntervalFromDuration = (
|
||||||
|
dateValue: number,
|
||||||
|
esValue: number,
|
||||||
|
esUnit: unitOfTime.Base,
|
||||||
|
timeZone: string
|
||||||
|
) => {
|
||||||
|
const date = moment.tz(dateValue, timeZone);
|
||||||
|
const startOfDate = moment.tz(date, timeZone).startOf(esUnit);
|
||||||
|
const endOfDate = moment.tz(date, timeZone).startOf(esUnit).add(esValue, esUnit);
|
||||||
|
return endOfDate.valueOf() - startOfDate.valueOf();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIntervalInMs = (
|
||||||
|
value: number,
|
||||||
|
esValue: number,
|
||||||
|
esUnit: unitOfTime.Base,
|
||||||
|
timeZone: string
|
||||||
|
): number => {
|
||||||
|
switch (esUnit) {
|
||||||
|
case 's':
|
||||||
|
return 1000 * esValue;
|
||||||
|
case 'ms':
|
||||||
|
return 1 * esValue;
|
||||||
|
default:
|
||||||
|
return findIntervalFromDuration(value, esValue, esUnit, timeZone);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the adjusted interval based on the data
|
||||||
|
*
|
||||||
|
* @param xValues sorted and unquie x values
|
||||||
|
* @param esValue
|
||||||
|
* @param esUnit
|
||||||
|
* @param timeZone
|
||||||
|
*/
|
||||||
|
export const getAdjustedInterval = (
|
||||||
|
xValues: number[],
|
||||||
|
esValue: number,
|
||||||
|
esUnit: unitOfTime.Base,
|
||||||
|
timeZone: string
|
||||||
|
): number => {
|
||||||
|
const newInterval = xValues.reduce((minInterval, currentXvalue, index) => {
|
||||||
|
let currentDiff = minInterval;
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
currentDiff = Math.abs(xValues[index - 1] - currentXvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleUnitInterval = getIntervalInMs(currentXvalue, esValue, esUnit, timeZone);
|
||||||
|
return Math.min(minInterval, singleUnitInterval, currentDiff);
|
||||||
|
}, Number.MAX_SAFE_INTEGER);
|
||||||
|
|
||||||
|
return newInterval > 0 ? newInterval : moment.duration(esValue, esUnit).asMilliseconds();
|
||||||
|
};
|
||||||
|
|
||||||
|
const partialDataText = i18n.translate('charts.partialData.bucketTooltipText', {
|
||||||
|
defaultMessage:
|
||||||
|
'The selected time range does not include this entire bucket. It might contain partial data.',
|
||||||
|
});
|
||||||
|
|
||||||
|
const Prompt = () => (
|
||||||
|
<EuiFlexGroup
|
||||||
|
alignItems="center"
|
||||||
|
className="dscHistogram__header--partial"
|
||||||
|
responsive={false}
|
||||||
|
gutterSize="xs"
|
||||||
|
>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiIcon type="iInCircle" />
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem>{partialDataText}</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const renderEndzoneTooltip = (
|
||||||
|
xInterval?: number,
|
||||||
|
domainStart?: number,
|
||||||
|
domainEnd?: number,
|
||||||
|
formatter?: (v: any) => string,
|
||||||
|
renderValue = true
|
||||||
|
) => (headerData: TooltipValue): JSX.Element | string => {
|
||||||
|
const headerDataValue = headerData.value;
|
||||||
|
const formattedValue = formatter ? formatter(headerDataValue) : headerDataValue;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(domainStart !== undefined && domainStart > headerDataValue) ||
|
||||||
|
(domainEnd !== undefined && xInterval !== undefined && domainEnd - xInterval < headerDataValue)
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Prompt />
|
||||||
|
{renderValue && (
|
||||||
|
<>
|
||||||
|
<EuiSpacer size="xs" />
|
||||||
|
<p>{formattedValue}</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderValue ? formattedValue : null;
|
||||||
|
};
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { BasicOptions } from './basic_options';
|
export { BasicOptions } from './basic_options';
|
||||||
export { ColorModes, Rotates } from './collections';
|
export { ColorMode, LabelRotation, defaultCountLabel } from './collections';
|
||||||
export { ColorRanges, SetColorRangeValue } from './color_ranges';
|
export { ColorRanges, SetColorRangeValue } from './color_ranges';
|
||||||
export { ColorSchemaOptions, SetColorSchemaOptionsValue } from './color_schema';
|
export { ColorSchemaOptions, SetColorSchemaOptionsValue } from './color_schema';
|
||||||
export { ColorSchemaParams, Labels, Style } from './types';
|
export { ColorSchemaParams, Labels, Style } from './types';
|
||||||
|
@ -28,3 +28,7 @@ export { RequiredNumberInputOption } from './required_number_input';
|
||||||
export { SelectOption } from './select';
|
export { SelectOption } from './select';
|
||||||
export { SwitchOption } from './switch';
|
export { SwitchOption } from './switch';
|
||||||
export { TextInputOption } from './text_input';
|
export { TextInputOption } from './text_input';
|
||||||
|
export { LegendToggle } from './legend_toggle';
|
||||||
|
export { ColorPicker } from './color_picker';
|
||||||
|
export { CurrentTime } from './current_time';
|
||||||
|
export * from './endzones';
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
.echLegend__toggle {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
margin: $euiSizeXS;
|
||||||
|
|
||||||
|
&--isOpen {
|
||||||
|
background-color: $euiColorLightestShade;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--position-left,
|
||||||
|
&--position-bottom {
|
||||||
|
left: auto;
|
||||||
|
bottom: auto;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { htmlIdGenerator, EuiButtonIcon } from '@elastic/eui';
|
||||||
|
import { Position } from '@elastic/charts';
|
||||||
|
|
||||||
|
import './legend_toggle.scss';
|
||||||
|
|
||||||
|
interface LegendToggleProps {
|
||||||
|
onClick: () => void;
|
||||||
|
showLegend: boolean;
|
||||||
|
legendPosition: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LegendToggleComponent = ({ onClick, showLegend, legendPosition }: LegendToggleProps) => {
|
||||||
|
const legendId = useMemo(() => htmlIdGenerator()('legend'), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiButtonIcon
|
||||||
|
type="button"
|
||||||
|
iconType="list"
|
||||||
|
color="subdued"
|
||||||
|
onClick={onClick}
|
||||||
|
className={classNames('echLegend__toggle', `echLegend__toggle--position-${legendPosition}`, {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
'echLegend__toggle--isOpen': showLegend,
|
||||||
|
})}
|
||||||
|
aria-label={i18n.translate('charts.legend.toggleLegendButtonAriaLabel', {
|
||||||
|
defaultMessage: 'Toggle legend',
|
||||||
|
})}
|
||||||
|
aria-expanded={showLegend}
|
||||||
|
aria-controls={legendId}
|
||||||
|
isSelected={showLegend}
|
||||||
|
data-test-subj="vislibToggleLegend"
|
||||||
|
title={i18n.translate('charts.legend.toggleLegendButtonTitle', {
|
||||||
|
defaultMessage: 'Toggle legend',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LegendToggle = memo(LegendToggleComponent);
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ColorSchemas } from '../color_maps';
|
import { ColorSchemas } from '../color_maps';
|
||||||
import { Rotates } from './collections';
|
import { LabelRotation } from './collections';
|
||||||
|
|
||||||
export interface ColorSchemaParams {
|
export interface ColorSchemaParams {
|
||||||
colorSchema: ColorSchemas;
|
colorSchema: ColorSchemas;
|
||||||
|
@ -29,8 +29,8 @@ export interface Labels {
|
||||||
color?: string;
|
color?: string;
|
||||||
filter?: boolean;
|
filter?: boolean;
|
||||||
overwriteColor?: boolean;
|
overwriteColor?: boolean;
|
||||||
rotate?: Rotates;
|
rotate?: LabelRotation;
|
||||||
show: boolean;
|
show?: boolean;
|
||||||
truncate?: number | null;
|
truncate?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,3 +20,4 @@
|
||||||
export * from './color_maps';
|
export * from './color_maps';
|
||||||
export * from './colors';
|
export * from './colors';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
export * from './utils';
|
||||||
|
|
20
src/plugins/charts/public/static/utils/index.ts
Normal file
20
src/plugins/charts/public/static/utils/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './transform_click_event';
|
238
src/plugins/charts/public/static/utils/transform_click_event.ts
Normal file
238
src/plugins/charts/public/static/utils/transform_click_event.ts
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
XYChartSeriesIdentifier,
|
||||||
|
GeometryValue,
|
||||||
|
XYBrushArea,
|
||||||
|
Accessor,
|
||||||
|
AccessorFn,
|
||||||
|
Datum,
|
||||||
|
} from '@elastic/charts';
|
||||||
|
|
||||||
|
import { RangeSelectContext, ValueClickContext } from '../../../../embeddable/public';
|
||||||
|
import { Datatable } from '../../../../expressions/public';
|
||||||
|
|
||||||
|
export interface ClickTriggerEvent {
|
||||||
|
name: 'filterBucket';
|
||||||
|
data: ValueClickContext['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BrushTriggerEvent {
|
||||||
|
name: 'brush';
|
||||||
|
data: RangeSelectContext['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns accessor value from string or function accessor
|
||||||
|
* @param datum
|
||||||
|
* @param accessor
|
||||||
|
*/
|
||||||
|
function getAccessorValue(datum: Datum, accessor: Accessor | AccessorFn) {
|
||||||
|
if (typeof accessor === 'function') {
|
||||||
|
return accessor(datum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return datum[accessor];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a little unorthodox, but using functional accessors makes it
|
||||||
|
* difficult to match the correct column. This creates a test object to throw
|
||||||
|
* an error when the target id is accessed, thus matcing the target column.
|
||||||
|
*/
|
||||||
|
function validateAccessorId(id: string, accessor: Accessor | AccessorFn) {
|
||||||
|
if (typeof accessor !== 'function') {
|
||||||
|
return id === accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedMessage = 'validateAccessorId matched';
|
||||||
|
|
||||||
|
try {
|
||||||
|
accessor({
|
||||||
|
get [id]() {
|
||||||
|
throw new Error(matchedMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
} catch ({ message }) {
|
||||||
|
return message === matchedMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups split accessors by their accessor string or function and related value
|
||||||
|
*
|
||||||
|
* @param splitAccessors
|
||||||
|
* @param splitSeriesAccessorFnMap
|
||||||
|
*/
|
||||||
|
const getAllSplitAccessors = (
|
||||||
|
splitAccessors: Map<string | number, string | number>,
|
||||||
|
splitSeriesAccessorFnMap?: Map<string | number, AccessorFn>
|
||||||
|
): Array<[accessor: Accessor | AccessorFn, value: string | number]> =>
|
||||||
|
[...splitAccessors.entries()].map(([key, value]) => [
|
||||||
|
splitSeriesAccessorFnMap?.get?.(key) ?? key,
|
||||||
|
value,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces matching column indexes
|
||||||
|
*
|
||||||
|
* @param xAccessor
|
||||||
|
* @param yAccessor
|
||||||
|
* @param splitAccessors
|
||||||
|
*/
|
||||||
|
const columnReducer = (
|
||||||
|
xAccessor: Accessor | AccessorFn | null,
|
||||||
|
yAccessor: Accessor | AccessorFn | null,
|
||||||
|
splitAccessors: AllSeriesAccessors
|
||||||
|
) => (
|
||||||
|
acc: Array<[index: number, id: string]>,
|
||||||
|
{ id }: Datatable['columns'][number],
|
||||||
|
index: number
|
||||||
|
): Array<[index: number, id: string]> => {
|
||||||
|
if (
|
||||||
|
(xAccessor !== null && validateAccessorId(id, xAccessor)) ||
|
||||||
|
(yAccessor !== null && validateAccessorId(id, yAccessor)) ||
|
||||||
|
splitAccessors.some(([accessor]) => validateAccessorId(id, accessor))
|
||||||
|
) {
|
||||||
|
acc.push([index, id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds matching row index for given accessors and geometry values
|
||||||
|
*
|
||||||
|
* @param geometry
|
||||||
|
* @param xAccessor
|
||||||
|
* @param yAccessor
|
||||||
|
* @param splitAccessors
|
||||||
|
*/
|
||||||
|
const rowFindPredicate = (
|
||||||
|
geometry: GeometryValue | null,
|
||||||
|
xAccessor: Accessor | AccessorFn | null,
|
||||||
|
yAccessor: Accessor | AccessorFn | null,
|
||||||
|
splitAccessors: AllSeriesAccessors
|
||||||
|
) => (row: Datatable['rows'][number]): boolean =>
|
||||||
|
(geometry === null ||
|
||||||
|
(xAccessor !== null &&
|
||||||
|
getAccessorValue(row, xAccessor) === geometry.x &&
|
||||||
|
yAccessor !== null &&
|
||||||
|
getAccessorValue(row, yAccessor) === geometry.y)) &&
|
||||||
|
[...splitAccessors].every(([accessor, value]) => getAccessorValue(row, accessor) === value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to transform `@elastic/charts` click event into filter action event
|
||||||
|
*
|
||||||
|
* @param table
|
||||||
|
* @param xAccessor
|
||||||
|
* @param splitSeriesAccessorFnMap needed when using `splitSeriesAccessors` as `AccessorFn`
|
||||||
|
* @param negate
|
||||||
|
*/
|
||||||
|
export const getFilterFromChartClickEventFn = (
|
||||||
|
table: Datatable,
|
||||||
|
xAccessor: Accessor | AccessorFn,
|
||||||
|
splitSeriesAccessorFnMap?: Map<string | number, AccessorFn>,
|
||||||
|
negate: boolean = false
|
||||||
|
) => (points: Array<[GeometryValue, XYChartSeriesIdentifier]>): ClickTriggerEvent => {
|
||||||
|
const data: ValueClickContext['data']['data'] = [];
|
||||||
|
|
||||||
|
points.forEach((point) => {
|
||||||
|
const [geometry, { yAccessor, splitAccessors }] = point;
|
||||||
|
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
|
||||||
|
const columns = table.columns.reduce<Array<[index: number, id: string]>>(
|
||||||
|
columnReducer(xAccessor, yAccessor, allSplitAccessors),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const row = table.rows.findIndex(
|
||||||
|
rowFindPredicate(geometry, xAccessor, yAccessor, allSplitAccessors)
|
||||||
|
);
|
||||||
|
const newData = columns.map(([column, id]) => ({
|
||||||
|
table,
|
||||||
|
column,
|
||||||
|
row,
|
||||||
|
value: table.rows?.[row]?.[id] ?? null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
data.push(...newData);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'filterBucket',
|
||||||
|
data: {
|
||||||
|
negate,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get filter action event from series
|
||||||
|
*/
|
||||||
|
export const getFilterFromSeriesFn = (table: Datatable) => (
|
||||||
|
{ splitAccessors }: XYChartSeriesIdentifier,
|
||||||
|
splitSeriesAccessorFnMap?: Map<string | number, AccessorFn>,
|
||||||
|
negate = false
|
||||||
|
): ClickTriggerEvent => {
|
||||||
|
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
|
||||||
|
const columns = table.columns.reduce<Array<[index: number, id: string]>>(
|
||||||
|
columnReducer(null, null, allSplitAccessors),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const row = table.rows.findIndex(rowFindPredicate(null, null, null, allSplitAccessors));
|
||||||
|
const data: ValueClickContext['data']['data'] = columns.map(([column, id]) => ({
|
||||||
|
table,
|
||||||
|
column,
|
||||||
|
row,
|
||||||
|
value: table.rows?.[row]?.[id] ?? null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'filterBucket',
|
||||||
|
data: {
|
||||||
|
negate,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to transform `@elastic/charts` brush event into brush action event
|
||||||
|
*/
|
||||||
|
export const getBrushFromChartBrushEventFn = (
|
||||||
|
table: Datatable,
|
||||||
|
xAccessor: Accessor | AccessorFn
|
||||||
|
) => ({ x: selectedRange }: XYBrushArea): BrushTriggerEvent => {
|
||||||
|
const [start, end] = selectedRange ?? [0, 0];
|
||||||
|
const range: [number, number] = [start, end];
|
||||||
|
const column = table.columns.findIndex(({ id }) => validateAccessorId(id, xAccessor));
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
table,
|
||||||
|
column,
|
||||||
|
range,
|
||||||
|
},
|
||||||
|
name: 'brush',
|
||||||
|
};
|
||||||
|
};
|
|
@ -17,25 +17,17 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui';
|
import moment, { unitOfTime } from 'moment-timezone';
|
||||||
import moment from 'moment-timezone';
|
|
||||||
import { unitOfTime } from 'moment';
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import lightEuiTheme from '@elastic/eui/dist/eui_theme_light.json';
|
|
||||||
import darkEuiTheme from '@elastic/eui/dist/eui_theme_dark.json';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AnnotationDomainTypes,
|
|
||||||
Axis,
|
Axis,
|
||||||
Chart,
|
Chart,
|
||||||
HistogramBarSeries,
|
HistogramBarSeries,
|
||||||
LineAnnotation,
|
|
||||||
Position,
|
Position,
|
||||||
ScaleType,
|
ScaleType,
|
||||||
Settings,
|
Settings,
|
||||||
RectAnnotation,
|
|
||||||
TooltipValue,
|
|
||||||
TooltipType,
|
TooltipType,
|
||||||
ElementClickListener,
|
ElementClickListener,
|
||||||
XYChartElementEvent,
|
XYChartElementEvent,
|
||||||
|
@ -43,12 +35,17 @@ import {
|
||||||
Theme,
|
Theme,
|
||||||
} from '@elastic/charts';
|
} from '@elastic/charts';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import { IUiSettingsClient } from 'kibana/public';
|
import { IUiSettingsClient } from 'kibana/public';
|
||||||
import { EuiChartThemeType } from '@elastic/eui/dist/eui_charts_theme';
|
import { EuiChartThemeType } from '@elastic/eui/dist/eui_charts_theme';
|
||||||
import { Subscription, combineLatest } from 'rxjs';
|
import { Subscription, combineLatest } from 'rxjs';
|
||||||
import { getServices } from '../../../kibana_services';
|
import { getServices } from '../../../kibana_services';
|
||||||
import { Chart as IChart } from '../helpers/point_series';
|
import { Chart as IChart } from '../helpers/point_series';
|
||||||
|
import {
|
||||||
|
CurrentTime,
|
||||||
|
Endzones,
|
||||||
|
getAdjustedInterval,
|
||||||
|
renderEndzoneTooltip,
|
||||||
|
} from '../../../../../charts/public';
|
||||||
|
|
||||||
export interface DiscoverHistogramProps {
|
export interface DiscoverHistogramProps {
|
||||||
chartData: IChart;
|
chartData: IChart;
|
||||||
|
@ -60,34 +57,6 @@ interface DiscoverHistogramState {
|
||||||
chartsBaseTheme: Theme;
|
chartsBaseTheme: Theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findIntervalFromDuration(
|
|
||||||
dateValue: number,
|
|
||||||
esValue: number,
|
|
||||||
esUnit: unitOfTime.Base,
|
|
||||||
timeZone: string
|
|
||||||
) {
|
|
||||||
const date = moment.tz(dateValue, timeZone);
|
|
||||||
const startOfDate = moment.tz(date, timeZone).startOf(esUnit);
|
|
||||||
const endOfDate = moment.tz(date, timeZone).startOf(esUnit).add(esValue, esUnit);
|
|
||||||
return endOfDate.valueOf() - startOfDate.valueOf();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIntervalInMs(
|
|
||||||
value: number,
|
|
||||||
esValue: number,
|
|
||||||
esUnit: unitOfTime.Base,
|
|
||||||
timeZone: string
|
|
||||||
): number {
|
|
||||||
switch (esUnit) {
|
|
||||||
case 's':
|
|
||||||
return 1000 * esValue;
|
|
||||||
case 'ms':
|
|
||||||
return 1 * esValue;
|
|
||||||
default:
|
|
||||||
return findIntervalFromDuration(value, esValue, esUnit, timeZone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTimezone(uiSettings: IUiSettingsClient) {
|
function getTimezone(uiSettings: IUiSettingsClient) {
|
||||||
if (uiSettings.isDefault('dateFormat:tz')) {
|
if (uiSettings.isDefault('dateFormat:tz')) {
|
||||||
const detectedTimezone = moment.tz.guess();
|
const detectedTimezone = moment.tz.guess();
|
||||||
|
@ -98,27 +67,6 @@ function getTimezone(uiSettings: IUiSettingsClient) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findMinInterval(
|
|
||||||
xValues: number[],
|
|
||||||
esValue: number,
|
|
||||||
esUnit: string,
|
|
||||||
timeZone: string
|
|
||||||
): number {
|
|
||||||
return xValues.reduce((minInterval, currentXvalue, index) => {
|
|
||||||
let currentDiff = minInterval;
|
|
||||||
if (index > 0) {
|
|
||||||
currentDiff = Math.abs(xValues[index - 1] - currentXvalue);
|
|
||||||
}
|
|
||||||
const singleUnitInterval = getIntervalInMs(
|
|
||||||
currentXvalue,
|
|
||||||
esValue,
|
|
||||||
esUnit as unitOfTime.Base,
|
|
||||||
timeZone
|
|
||||||
);
|
|
||||||
return Math.min(minInterval, singleUnitInterval, currentDiff);
|
|
||||||
}, Number.MAX_SAFE_INTEGER);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DiscoverHistogram extends Component<DiscoverHistogramProps, DiscoverHistogramState> {
|
export class DiscoverHistogram extends Component<DiscoverHistogramProps, DiscoverHistogramState> {
|
||||||
public static propTypes = {
|
public static propTypes = {
|
||||||
chartData: PropTypes.object,
|
chartData: PropTypes.object,
|
||||||
|
@ -132,10 +80,10 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.subscription = combineLatest(
|
this.subscription = combineLatest([
|
||||||
getServices().theme.chartsTheme$,
|
getServices().theme.chartsTheme$,
|
||||||
getServices().theme.chartsBaseTheme$
|
getServices().theme.chartsBaseTheme$,
|
||||||
).subscribe(([chartsTheme, chartsBaseTheme]) =>
|
]).subscribe(([chartsTheme, chartsBaseTheme]) =>
|
||||||
this.setState({ chartsTheme, chartsBaseTheme })
|
this.setState({ chartsTheme, chartsBaseTheme })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,40 +119,6 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
||||||
return moment(val).format(xAxisFormat);
|
return moment(val).format(xAxisFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
public renderBarTooltip = (xInterval: number, domainStart: number, domainEnd: number) => (
|
|
||||||
headerData: TooltipValue
|
|
||||||
): JSX.Element | string => {
|
|
||||||
const headerDataValue = headerData.value;
|
|
||||||
const formattedValue = this.formatXValue(headerDataValue);
|
|
||||||
|
|
||||||
const partialDataText = i18n.translate('discover.histogram.partialData.bucketTooltipText', {
|
|
||||||
defaultMessage:
|
|
||||||
'The selected time range does not include this entire bucket, it may contain partial data.',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (headerDataValue < domainStart || headerDataValue + xInterval > domainEnd) {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<EuiFlexGroup
|
|
||||||
alignItems="center"
|
|
||||||
className="dscHistogram__header--partial"
|
|
||||||
responsive={false}
|
|
||||||
gutterSize="xs"
|
|
||||||
>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiIcon type="iInCircle" />
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem>{partialDataText}</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
<EuiSpacer size="xs" />
|
|
||||||
<p>{formattedValue}</p>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const uiSettings = getServices().uiSettings;
|
const uiSettings = getServices().uiSettings;
|
||||||
const timeZone = getTimezone(uiSettings);
|
const timeZone = getTimezone(uiSettings);
|
||||||
|
@ -216,8 +130,9 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = chartData.values;
|
const data = chartData.values;
|
||||||
|
const isDarkMode = uiSettings.get('theme:darkMode');
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Deprecation: [interval] on [date_histogram] is deprecated, use [fixed_interval] or [calendar_interval].
|
* Deprecation: [interval] on [date_histogram] is deprecated, use [fixed_interval] or [calendar_interval].
|
||||||
* see https://github.com/elastic/kibana/issues/27410
|
* see https://github.com/elastic/kibana/issues/27410
|
||||||
* TODO: Once the Discover query has been update, we should change the below to use the new field
|
* TODO: Once the Discover query has been update, we should change the below to use the new field
|
||||||
|
@ -232,61 +147,21 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
||||||
const domainStart = domain.min.valueOf();
|
const domainStart = domain.min.valueOf();
|
||||||
const domainEnd = domain.max.valueOf();
|
const domainEnd = domain.max.valueOf();
|
||||||
|
|
||||||
const domainMin = data[0]?.x > domainStart ? domainStart : data[0]?.x;
|
const domainMin = Math.min(data[0]?.x, domainStart);
|
||||||
const domainMax = domainEnd - xInterval > lastXValue ? domainEnd - xInterval : lastXValue;
|
const domainMax = Math.max(domainEnd - xInterval, lastXValue);
|
||||||
|
|
||||||
const xDomain = {
|
const xDomain = {
|
||||||
min: domainMin,
|
min: domainMin,
|
||||||
max: domainMax,
|
max: domainMax,
|
||||||
minInterval: findMinInterval(xValues, intervalESValue, intervalESUnit, timeZone),
|
minInterval: getAdjustedInterval(
|
||||||
|
xValues,
|
||||||
|
intervalESValue,
|
||||||
|
intervalESUnit as unitOfTime.Base,
|
||||||
|
timeZone
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Domain end of 'now' will be milliseconds behind current time, so we extend time by 1 minute and check if
|
|
||||||
// the annotation is within this range; if so, the line annotation uses the domainEnd as its value
|
|
||||||
const now = moment();
|
|
||||||
const isAnnotationAtEdge = moment(domainEnd).add(60000).isAfter(now) && now.isAfter(domainEnd);
|
|
||||||
const lineAnnotationValue = isAnnotationAtEdge ? domainEnd : now;
|
|
||||||
|
|
||||||
const lineAnnotationData = [
|
|
||||||
{
|
|
||||||
dataValue: lineAnnotationValue,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const isDarkMode = uiSettings.get('theme:darkMode');
|
|
||||||
|
|
||||||
const lineAnnotationStyle = {
|
|
||||||
line: {
|
|
||||||
strokeWidth: 2,
|
|
||||||
stroke: isDarkMode ? darkEuiTheme.euiColorDanger : lightEuiTheme.euiColorDanger,
|
|
||||||
opacity: 0.7,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const rectAnnotations = [];
|
|
||||||
if (domainStart !== domainMin) {
|
|
||||||
rectAnnotations.push({
|
|
||||||
coordinates: {
|
|
||||||
x1: domainStart,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (domainEnd !== domainMax) {
|
|
||||||
rectAnnotations.push({
|
|
||||||
coordinates: {
|
|
||||||
x0: domainEnd,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const rectAnnotationStyle = {
|
|
||||||
stroke: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade,
|
|
||||||
strokeWidth: 0,
|
|
||||||
opacity: isDarkMode ? 0.6 : 0.2,
|
|
||||||
fill: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tooltipProps = {
|
const tooltipProps = {
|
||||||
headerFormatter: this.renderBarTooltip(xInterval, domainStart, domainEnd),
|
headerFormatter: renderEndzoneTooltip(xInterval, domainStart, domainEnd, this.formatXValue),
|
||||||
type: TooltipType.VerticalCursor,
|
type: TooltipType.VerticalCursor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -313,19 +188,14 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
||||||
tickFormat={this.formatXValue}
|
tickFormat={this.formatXValue}
|
||||||
ticks={10}
|
ticks={10}
|
||||||
/>
|
/>
|
||||||
<LineAnnotation
|
<CurrentTime isDarkMode={isDarkMode} domainEnd={domainEnd} />
|
||||||
id="line-annotation"
|
<Endzones
|
||||||
domainType={AnnotationDomainTypes.XDomain}
|
isDarkMode={isDarkMode}
|
||||||
dataValues={lineAnnotationData}
|
domainStart={domainStart}
|
||||||
hideTooltips={true}
|
domainEnd={domainEnd}
|
||||||
style={lineAnnotationStyle}
|
interval={xDomain.minInterval}
|
||||||
/>
|
domainMin={xDomain.min}
|
||||||
<RectAnnotation
|
domainMax={xDomain.max}
|
||||||
dataValues={rectAnnotations}
|
|
||||||
id="rect-annotation"
|
|
||||||
zIndex={2}
|
|
||||||
style={rectAnnotationStyle}
|
|
||||||
hideTooltips={true}
|
|
||||||
/>
|
/>
|
||||||
<HistogramBarSeries
|
<HistogramBarSeries
|
||||||
id="discover-histogram"
|
id="discover-histogram"
|
||||||
|
|
|
@ -34,7 +34,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[eCommerce] Sales by Category',
|
defaultMessage: '[eCommerce] Sales by Category',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[eCommerce] Sales by Category","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Sum of total_quantity"}}],"seriesParams":[{"show":"true","type":"area","mode":"stacked","data":{"label":"Sum of total_quantity","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"linear","valueAxis":"ValueAxis-1"}],"addTooltip":true,"addLegend":true,"legendPosition":"top","times":[],"addTimeMarker":false},"aggs":[{"id":"1","enabled":true,"type":"sum","schema":"metric","params":{"field":"total_quantity"}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"order_date","interval":"auto","time_zone":"America/New_York","drop_partials":false,"customInterval":"2h","min_doc_count":1,"extended_bounds":{}}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"category.keyword","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing"}}]}',
|
'{"title":"[eCommerce] Sales by Category","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Sum of total_quantity"}}],"seriesParams":[{"show":"true","type":"area","mode":"stacked","data":{"label":"Sum of total_quantity","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"linear","valueAxis":"ValueAxis-1"}],"addTooltip":true,"addLegend":true,"legendPosition":"top","times":[],"addTimeMarker":false,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"sum","schema":"metric","params":{"field":"total_quantity"}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"order_date","interval":"auto","drop_partials":false,"min_doc_count":1,"extended_bounds":{}}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"category.keyword","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing"}}]}',
|
||||||
uiStateJSON: '{}',
|
uiStateJSON: '{}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Flights] Flight Count and Average Ticket Price',
|
defaultMessage: '[Flights] Flight Count and Average Ticket Price',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Flights] Flight Count and Average Ticket Price","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Average Ticket Price"}},{"id":"ValueAxis-2","name":"RightAxis-1","type":"value","position":"right","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Flight Count"}}],"seriesParams":[{"show":true,"mode":"stacked","type":"area","drawLinesBetweenPoints":true,"showCircles":false,"interpolate":"linear","lineWidth":2,"data":{"id":"5","label":"Flight Count"},"valueAxis":"ValueAxis-2"},{"show":true,"mode":"stacked","type":"line","drawLinesBetweenPoints":false,"showCircles":true,"interpolate":"linear","data":{"id":"4","label":"Average Ticket Price"},"valueAxis":"ValueAxis-1","lineWidth":2}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"radiusRatio":13},"aggs":[{"id":"3","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","customInterval":"2h","min_doc_count":1,"extended_bounds":{}}},{"id":"5","enabled":true,"type":"count","schema":"metric","params":{"customLabel":"Flight Count"}},{"id":"4","enabled":true,"type":"avg","schema":"metric","params":{"field":"AvgTicketPrice","customLabel":"Average Ticket Price"}},{"id":"2","enabled":true,"type":"avg","schema":"radius","params":{"field":"AvgTicketPrice"}}]}',
|
'{"title":"[Flights] Flight Count and Average Ticket Price","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Average Ticket Price"}},{"id":"ValueAxis-2","name":"RightAxis-1","type":"value","position":"right","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Flight Count"}}],"seriesParams":[{"show":true,"mode":"stacked","type":"area","drawLinesBetweenPoints":true,"showCircles":false,"interpolate":"linear","lineWidth":2,"data":{"id":"5","label":"Flight Count"},"valueAxis":"ValueAxis-2"},{"show":true,"mode":"stacked","type":"line","drawLinesBetweenPoints":false,"showCircles":true,"interpolate":"linear","data":{"id":"4","label":"Average Ticket Price"},"valueAxis":"ValueAxis-1","lineWidth":2}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"radiusRatio":13,"detailedTooltip":true},"aggs":[{"id":"3","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","min_doc_count":1,"extended_bounds":{}}},{"id":"5","enabled":true,"type":"count","schema":"metric","params":{"customLabel":"Flight Count"}},{"id":"4","enabled":true,"type":"avg","schema":"metric","params":{"field":"AvgTicketPrice","customLabel":"Average Ticket Price"}},{"id":"2","enabled":true,"type":"avg","schema":"radius","params":{"field":"AvgTicketPrice"}}]}',
|
||||||
uiStateJSON:
|
uiStateJSON:
|
||||||
'{"vis":{"legendOpen":true,"colors":{"Average Ticket Price":"#629E51","Flight Count":"#AEA2E0"}}}',
|
'{"vis":{"legendOpen":true,"colors":{"Average Ticket Price":"#629E51","Flight Count":"#AEA2E0"}}}',
|
||||||
description: '',
|
description: '',
|
||||||
|
@ -133,7 +133,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Flights] Delay Type',
|
defaultMessage: '[Flights] Delay Type',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Flights] Delay Type","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"cardinal","valueAxis":"ValueAxis-1"}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","customInterval":"2h","min_doc_count":1,"extended_bounds":{}}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"FlightDelayType","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing"}}]}',
|
'{"title":"[Flights] Delay Type","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"cardinal","valueAxis":"ValueAxis-1"}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","min_doc_count":1,"extended_bounds":{}}},{"id":"3","enabled":true,"type":"terms","schema":"group","params":{"field":"FlightDelayType","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing"}}]}',
|
||||||
uiStateJSON: '{}',
|
uiStateJSON: '{}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -176,7 +176,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Flights] Delay Buckets',
|
defaultMessage: '[Flights] Delay Buckets',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Flights] Delay Buckets","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"histogram","schema":"segment","params":{"field":"FlightDelayMin","interval":30,"extended_bounds":{},"customLabel":"Flight Delay Minutes"}}]}',
|
'{"title":"[Flights] Delay Buckets","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}},{"id":"2","enabled":true,"type":"histogram","schema":"segment","params":{"field":"FlightDelayMin","interval":30,"extended_bounds":{},"customLabel":"Flight Delay Minutes"}}]}',
|
||||||
uiStateJSON: '{"vis":{"legendOpen":false}}',
|
uiStateJSON: '{"vis":{"legendOpen":false}}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -198,7 +198,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Flights] Flight Delays',
|
defaultMessage: '[Flights] Flight Delays',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Flights] Flight Delays","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"left","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"BottomAxis-1","type":"value","position":"bottom","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{"customLabel":""}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"FlightDelay","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Flight Delays"}}]}',
|
'{"title":"[Flights] Flight Delays","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"left","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"BottomAxis-1","type":"value","position":"bottom","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{"customLabel":""}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"FlightDelay","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Flight Delays"}}]}',
|
||||||
uiStateJSON: '{}',
|
uiStateJSON: '{}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -220,7 +220,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Flights] Flight Cancellations',
|
defaultMessage: '[Flights] Flight Cancellations',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Flights] Flight Cancellations","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"left","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"BottomAxis-1","type":"value","position":"bottom","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{"customLabel":""}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"Cancelled","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Flight Cancellations"}}]}',
|
'{"title":"[Flights] Flight Cancellations","type":"histogram","params":{"type":"histogram","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"left","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"BottomAxis-1","type":"value","position":"bottom","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Count"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Count","id":"1"},"valueAxis":"ValueAxis-1","drawLinesBetweenPoints":true,"showCircles":true}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{"customLabel":""}},{"id":"2","enabled":true,"type":"terms","schema":"segment","params":{"field":"Cancelled","size":5,"order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","missingBucket":false,"missingBucketLabel":"Missing","customLabel":"Flight Cancellations"}}]}',
|
||||||
uiStateJSON: '{}',
|
uiStateJSON: '{}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
||||||
defaultMessage: '[Logs] Unique Visitors vs. Average Bytes',
|
defaultMessage: '[Logs] Unique Visitors vs. Average Bytes',
|
||||||
}),
|
}),
|
||||||
visState:
|
visState:
|
||||||
'{"title":"[Logs] Unique Visitors vs. Average Bytes","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Avg. Bytes"}},{"id":"ValueAxis-2","name":"RightAxis-1","type":"value","position":"right","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Unique Visitors"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Avg. Bytes","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"linear","valueAxis":"ValueAxis-1"},{"show":true,"mode":"stacked","type":"line","drawLinesBetweenPoints":false,"showCircles":true,"interpolate":"linear","data":{"id":"2","label":"Unique Visitors"},"valueAxis":"ValueAxis-2"}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"radiusRatio":17},"aggs":[{"id":"1","enabled":true,"type":"avg","schema":"metric","params":{"field":"bytes","customLabel":"Avg. Bytes"}},{"id":"2","enabled":true,"type":"cardinality","schema":"metric","params":{"field":"clientip","customLabel":"Unique Visitors"}},{"id":"3","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","time_zone":"America/Los_Angeles","customInterval":"2h","min_doc_count":1,"extended_bounds":{}}},{"id":"4","enabled":true,"type":"count","schema":"radius","params":{}}]}',
|
'{"title":"[Logs] Unique Visitors vs. Average Bytes","type":"area","params":{"type":"area","grid":{"categoryLines":false,"style":{"color":"#eee"}},"categoryAxes":[{"id":"CategoryAxis-1","type":"category","position":"bottom","show":true,"style":{},"scale":{"type":"linear"},"labels":{"show":true,"truncate":100,"filter":true},"title":{}}],"valueAxes":[{"id":"ValueAxis-1","name":"LeftAxis-1","type":"value","position":"left","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Avg. Bytes"}},{"id":"ValueAxis-2","name":"RightAxis-1","type":"value","position":"right","show":true,"style":{},"scale":{"type":"linear","mode":"normal"},"labels":{"show":true,"rotate":0,"filter":false,"truncate":100},"title":{"text":"Unique Visitors"}}],"seriesParams":[{"show":"true","type":"histogram","mode":"stacked","data":{"label":"Avg. Bytes","id":"1"},"drawLinesBetweenPoints":true,"showCircles":true,"interpolate":"linear","valueAxis":"ValueAxis-1"},{"show":true,"mode":"stacked","type":"line","drawLinesBetweenPoints":false,"showCircles":true,"interpolate":"linear","data":{"id":"2","label":"Unique Visitors"},"valueAxis":"ValueAxis-2"}],"addTooltip":true,"addLegend":true,"legendPosition":"right","times":[],"addTimeMarker":false,"radiusRatio":17,"detailedTooltip":true},"aggs":[{"id":"1","enabled":true,"type":"avg","schema":"metric","params":{"field":"bytes","customLabel":"Avg. Bytes"}},{"id":"2","enabled":true,"type":"cardinality","schema":"metric","params":{"field":"clientip","customLabel":"Unique Visitors"}},{"id":"3","enabled":true,"type":"date_histogram","schema":"segment","params":{"field":"timestamp","interval":"auto","min_doc_count":1,"extended_bounds":{}}},{"id":"4","enabled":true,"type":"count","schema":"radius","params":{}}]}',
|
||||||
uiStateJSON: '{"vis":{"colors":{"Avg. Bytes":"#70DBED","Unique Visitors":"#0A437C"}}}',
|
uiStateJSON: '{"vis":{"colors":{"Avg. Bytes":"#70DBED","Unique Visitors":"#0A437C"}}}',
|
||||||
description: '',
|
description: '',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|
|
@ -76,10 +76,17 @@ function DefaultEditorAggAdd({
|
||||||
</EuiButtonEmpty>
|
</EuiButtonEmpty>
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSchemaDisabled = (schema: Schema): boolean => {
|
const isMaxedCount = (schema: Schema): boolean => {
|
||||||
const count = group.filter((agg) => agg.schema === schema.name).length;
|
const count = group.filter((agg) => agg.schema === schema.name).length;
|
||||||
return count >= schema.max;
|
return count >= schema.max;
|
||||||
};
|
};
|
||||||
|
const isSchemaDisabled = (schema: Schema, maxedCount: boolean): boolean => {
|
||||||
|
return schema.disabled ?? maxedCount;
|
||||||
|
};
|
||||||
|
const maxTooltipText = i18n.translate('visDefaultEditor.aggAdd.maxBuckets', {
|
||||||
|
defaultMessage: 'Max {groupNameLabel} count reached',
|
||||||
|
values: { groupNameLabel },
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiFlexGroup justifyContent="center" responsive={false}>
|
<EuiFlexGroup justifyContent="center" responsive={false}>
|
||||||
|
@ -109,16 +116,21 @@ function DefaultEditorAggAdd({
|
||||||
)}
|
)}
|
||||||
</EuiPopoverTitle>
|
</EuiPopoverTitle>
|
||||||
<EuiContextMenuPanel
|
<EuiContextMenuPanel
|
||||||
items={schemas.map((schema) => (
|
items={schemas.map((schema) => {
|
||||||
<EuiContextMenuItem
|
const maxedCount = isMaxedCount(schema);
|
||||||
key={`${schema.name}_${schema.title}`}
|
|
||||||
data-test-subj={`visEditorAdd_${groupName}_${schema.title}`}
|
return (
|
||||||
disabled={isPopoverOpen && isSchemaDisabled(schema)}
|
<EuiContextMenuItem
|
||||||
onClick={() => onSelectSchema(schema)}
|
key={`${schema.name}_${schema.title}`}
|
||||||
>
|
data-test-subj={`visEditorAdd_${groupName}_${schema.title}`}
|
||||||
{schema.title}
|
disabled={isPopoverOpen && isSchemaDisabled(schema, maxedCount)}
|
||||||
</EuiContextMenuItem>
|
onClick={() => onSelectSchema(schema)}
|
||||||
))}
|
toolTipContent={schema.tooltip ?? (maxedCount ? maxTooltipText : undefined)}
|
||||||
|
>
|
||||||
|
{schema.title}
|
||||||
|
</EuiContextMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</EuiPopover>
|
</EuiPopover>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { Vis } from 'src/plugins/visualizations/public';
|
import { Vis } from '../../../../visualizations/public';
|
||||||
import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab';
|
|
||||||
import { VisOptionsProps } from '../../vis_options_props';
|
import { VisOptionsProps } from '../../vis_options_props';
|
||||||
|
import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab';
|
||||||
|
|
||||||
export interface OptionTab {
|
export interface OptionTab {
|
||||||
editor: React.ComponentType<VisOptionsProps | DefaultEditorDataTabProps>;
|
editor: React.ComponentType<VisOptionsProps | DefaultEditorDataTabProps>;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { EuiErrorBoundary, EuiLoadingChart } from '@elastic/eui';
|
||||||
import { EditorRenderProps, IEditorController } from 'src/plugins/visualize/public';
|
import { EditorRenderProps, IEditorController } from 'src/plugins/visualize/public';
|
||||||
import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public';
|
import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const DefaultEditor = lazy(() => import('./default_editor'));
|
const DefaultEditor = lazy(() => import('./default_editor'));
|
||||||
|
|
||||||
class DefaultEditorController implements IEditorController {
|
class DefaultEditorController implements IEditorController {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ReactNode } from 'react';
|
||||||
import _, { defaults } from 'lodash';
|
import _, { defaults } from 'lodash';
|
||||||
|
|
||||||
import { Optional } from '@kbn/utility-types';
|
import { Optional } from '@kbn/utility-types';
|
||||||
|
@ -42,6 +43,8 @@ export interface Schema {
|
||||||
hideCustomLabel?: boolean;
|
hideCustomLabel?: boolean;
|
||||||
mustBeFirst?: boolean;
|
mustBeFirst?: boolean;
|
||||||
aggSettings?: any;
|
aggSettings?: any;
|
||||||
|
disabled?: boolean;
|
||||||
|
tooltip?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Schemas implements ISchemas {
|
export class Schemas implements ISchemas {
|
||||||
|
|
|
@ -42,12 +42,14 @@ describe('markdown vis toExpressionAst function', () => {
|
||||||
|
|
||||||
it('without params', () => {
|
it('without params', () => {
|
||||||
vis.params = {};
|
vis.params = {};
|
||||||
|
// @ts-expect-error
|
||||||
const actual = toExpressionAst(vis);
|
const actual = toExpressionAst(vis);
|
||||||
expect(actual).toMatchSnapshot();
|
expect(actual).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with params', () => {
|
it('with params', () => {
|
||||||
vis.params = { markdown: '### my markdown', fontSize: 15, openLinksInNewTab: true };
|
vis.params = { markdown: '### my markdown', fontSize: 15, openLinksInNewTab: true };
|
||||||
|
// @ts-expect-error
|
||||||
const actual = toExpressionAst(vis);
|
const actual = toExpressionAst(vis);
|
||||||
expect(actual).toMatchSnapshot();
|
expect(actual).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Vis } from '../../visualizations/public';
|
import { VisToExpressionAst } from '../../visualizations/public';
|
||||||
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
|
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
|
||||||
import { MarkdownVisExpressionFunctionDefinition } from './markdown_fn';
|
import { MarkdownVisExpressionFunctionDefinition } from './markdown_fn';
|
||||||
|
|
||||||
export const toExpressionAst = (vis: Vis) => {
|
export const toExpressionAst: VisToExpressionAst = (vis) => {
|
||||||
const { markdown, fontSize, openLinksInNewTab } = vis.params;
|
const { markdown, fontSize, openLinksInNewTab } = vis.params;
|
||||||
|
|
||||||
const markdownVis = buildExpressionFunction<MarkdownVisExpressionFunctionDefinition>(
|
const markdownVis = buildExpressionFunction<MarkdownVisExpressionFunctionDefinition>(
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
||||||
import {
|
import {
|
||||||
ColorModes,
|
ColorMode,
|
||||||
ColorRanges,
|
ColorRanges,
|
||||||
ColorSchemaOptions,
|
ColorSchemaOptions,
|
||||||
SwitchOption,
|
SwitchOption,
|
||||||
|
@ -86,7 +86,7 @@ function MetricVisOptions({
|
||||||
);
|
);
|
||||||
|
|
||||||
const setColorMode: EuiButtonGroupProps['onChange'] = useCallback(
|
const setColorMode: EuiButtonGroupProps['onChange'] = useCallback(
|
||||||
(id: string) => setMetricValue('metricColorMode', id as ColorModes),
|
(id: string) => setMetricValue('metricColorMode', id as ColorMode),
|
||||||
[setMetricValue]
|
[setMetricValue]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ function MetricVisOptions({
|
||||||
colorSchemas={vis.type.editorConfig.collections.colorSchemas}
|
colorSchemas={vis.type.editorConfig.collections.colorSchemas}
|
||||||
disabled={
|
disabled={
|
||||||
stateParams.metric.colorsRange.length === 1 ||
|
stateParams.metric.colorsRange.length === 1 ||
|
||||||
stateParams.metric.metricColorMode === ColorModes.NONE
|
stateParams.metric.metricColorMode === ColorMode.None
|
||||||
}
|
}
|
||||||
invertColors={stateParams.metric.invertColors}
|
invertColors={stateParams.metric.invertColors}
|
||||||
setValue={setMetricValue as SetColorSchemaOptionsValue}
|
setValue={setMetricValue as SetColorSchemaOptionsValue}
|
||||||
|
|
|
@ -27,14 +27,14 @@ import {
|
||||||
Style,
|
Style,
|
||||||
} from '../../expressions/public';
|
} from '../../expressions/public';
|
||||||
import { visType, DimensionsVisParam, VisParams } from './types';
|
import { visType, DimensionsVisParam, VisParams } from './types';
|
||||||
import { ColorSchemas, vislibColorMaps, ColorModes } from '../../charts/public';
|
import { ColorSchemas, vislibColorMaps, ColorMode } from '../../charts/public';
|
||||||
|
|
||||||
export type Input = Datatable;
|
export type Input = Datatable;
|
||||||
|
|
||||||
interface Arguments {
|
interface Arguments {
|
||||||
percentageMode: boolean;
|
percentageMode: boolean;
|
||||||
colorSchema: ColorSchemas;
|
colorSchema: ColorSchemas;
|
||||||
colorMode: ColorModes;
|
colorMode: ColorMode;
|
||||||
useRanges: boolean;
|
useRanges: boolean;
|
||||||
invertColors: boolean;
|
invertColors: boolean;
|
||||||
showLabels: boolean;
|
showLabels: boolean;
|
||||||
|
@ -86,7 +86,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({
|
||||||
colorMode: {
|
colorMode: {
|
||||||
types: ['string'],
|
types: ['string'],
|
||||||
default: '"None"',
|
default: '"None"',
|
||||||
options: [ColorModes.NONE, ColorModes.LABELS, ColorModes.BACKGROUND],
|
options: [ColorMode.None, ColorMode.Labels, ColorMode.Background],
|
||||||
help: i18n.translate('visTypeMetric.function.colorMode.help', {
|
help: i18n.translate('visTypeMetric.function.colorMode.help', {
|
||||||
defaultMessage: 'Which part of metric to color',
|
defaultMessage: 'Which part of metric to color',
|
||||||
}),
|
}),
|
||||||
|
@ -197,8 +197,8 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({
|
||||||
invertColors: args.invertColors,
|
invertColors: args.invertColors,
|
||||||
style: {
|
style: {
|
||||||
bgFill: args.bgFill,
|
bgFill: args.bgFill,
|
||||||
bgColor: args.colorMode === ColorModes.BACKGROUND,
|
bgColor: args.colorMode === ColorMode.Background,
|
||||||
labelColor: args.colorMode === ColorModes.LABELS,
|
labelColor: args.colorMode === ColorMode.Labels,
|
||||||
subText: args.subText,
|
subText: args.subText,
|
||||||
fontSize,
|
fontSize,
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
|
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
|
||||||
import { MetricVisOptions } from './components/metric_vis_options';
|
import { MetricVisOptions } from './components/metric_vis_options';
|
||||||
import { ColorSchemas, colorSchemas, ColorModes } from '../../charts/public';
|
import { ColorSchemas, colorSchemas, ColorMode } from '../../charts/public';
|
||||||
import { AggGroupNames } from '../../data/public';
|
import { AggGroupNames } from '../../data/public';
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
import { Schemas } from '../../vis_default_editor/public';
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
|
@ -42,7 +42,7 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
|
||||||
percentageMode: false,
|
percentageMode: false,
|
||||||
useRanges: false,
|
useRanges: false,
|
||||||
colorSchema: ColorSchemas.GreenToRed,
|
colorSchema: ColorSchemas.GreenToRed,
|
||||||
metricColorMode: ColorModes.NONE,
|
metricColorMode: ColorMode.None,
|
||||||
colorsRange: [{ from: 0, to: 10000 }],
|
colorsRange: [{ from: 0, to: 10000 }],
|
||||||
labels: {
|
labels: {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -62,19 +62,19 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
|
||||||
collections: {
|
collections: {
|
||||||
metricColorMode: [
|
metricColorMode: [
|
||||||
{
|
{
|
||||||
id: ColorModes.NONE,
|
id: ColorMode.None,
|
||||||
label: i18n.translate('visTypeMetric.colorModes.noneOptionLabel', {
|
label: i18n.translate('visTypeMetric.colorModes.noneOptionLabel', {
|
||||||
defaultMessage: 'None',
|
defaultMessage: 'None',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ColorModes.LABELS,
|
id: ColorMode.Labels,
|
||||||
label: i18n.translate('visTypeMetric.colorModes.labelsOptionLabel', {
|
label: i18n.translate('visTypeMetric.colorModes.labelsOptionLabel', {
|
||||||
defaultMessage: 'Labels',
|
defaultMessage: 'Labels',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ColorModes.BACKGROUND,
|
id: ColorMode.Background,
|
||||||
label: i18n.translate('visTypeMetric.colorModes.backgroundOptionLabel', {
|
label: i18n.translate('visTypeMetric.colorModes.backgroundOptionLabel', {
|
||||||
defaultMessage: 'Background',
|
defaultMessage: 'Background',
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
import { Range } from '../../expressions/public';
|
import { Range } from '../../expressions/public';
|
||||||
import { SchemaConfig } from '../../visualizations/public';
|
import { SchemaConfig } from '../../visualizations/public';
|
||||||
import { ColorModes, Labels, Style, ColorSchemas } from '../../charts/public';
|
import { ColorMode, Labels, Style, ColorSchemas } from '../../charts/public';
|
||||||
|
|
||||||
export const visType = 'metric';
|
export const visType = 'metric';
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export interface MetricVisParam {
|
||||||
percentageMode: boolean;
|
percentageMode: boolean;
|
||||||
useRanges: boolean;
|
useRanges: boolean;
|
||||||
colorSchema: ColorSchemas;
|
colorSchema: ColorSchemas;
|
||||||
metricColorMode: ColorModes;
|
metricColorMode: ColorMode;
|
||||||
colorsRange: Range[];
|
colorsRange: Range[];
|
||||||
labels: Labels;
|
labels: Labels;
|
||||||
invertColors: boolean;
|
invertColors: boolean;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/area_decorator.js <AreaSeriesDecorator /> should render and match a snapshot 1`] = `
|
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/area_decorator.js <AreaSeriesDecorator /> should render and match a snapshot 1`] = `
|
||||||
<AreaSeries
|
<Connect(SpecInstance)
|
||||||
areaSeriesStyle={
|
areaSeriesStyle={
|
||||||
Object {
|
Object {
|
||||||
"area": Object {
|
"area": Object {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/bar_decorator.js <BarSeriesDecorator /> should render and match a snapshot 1`] = `
|
exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/decorators/bar_decorator.js <BarSeriesDecorator /> should render and match a snapshot 1`] = `
|
||||||
<BarSeries
|
<Connect(SpecInstance)
|
||||||
barSeriesStyle={
|
barSeriesStyle={
|
||||||
Object {
|
Object {
|
||||||
"rect": Object {
|
"rect": Object {
|
||||||
|
|
|
@ -4,6 +4,5 @@
|
||||||
"server": true,
|
"server": true,
|
||||||
"ui": true,
|
"ui": true,
|
||||||
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"],
|
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"],
|
||||||
"optionalPlugins": ["visTypeXy"],
|
"requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy"]
|
||||||
"requiredBundles": ["kibanaUtils", "visDefaultEditor"]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,172 +17,14 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||||
// @ts-ignore
|
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
import { palettes } from '@elastic/eui/lib/services';
|
|
||||||
// @ts-ignore
|
|
||||||
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
|
||||||
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
|
||||||
import {
|
|
||||||
Positions,
|
|
||||||
ChartTypes,
|
|
||||||
ChartModes,
|
|
||||||
InterpolationModes,
|
|
||||||
AxisTypes,
|
|
||||||
ScaleTypes,
|
|
||||||
AxisModes,
|
|
||||||
ThresholdLineStyles,
|
|
||||||
getConfigCollections,
|
|
||||||
} from './utils/collections';
|
|
||||||
import { getAreaOptionTabs, countLabel } from './utils/common_config';
|
|
||||||
import { Rotates } from '../../charts/public';
|
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
import { BasicVislibParams } from './types';
|
import { BasicVislibParams } from './types';
|
||||||
|
|
||||||
export const areaVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
export const areaVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
name: 'area',
|
...(xyVisTypes.area() as BaseVisTypeOptions<BasicVislibParams>),
|
||||||
title: i18n.translate('visTypeVislib.area.areaTitle', { defaultMessage: 'Area' }),
|
|
||||||
icon: 'visArea',
|
|
||||||
description: i18n.translate('visTypeVislib.area.areaDescription', {
|
|
||||||
defaultMessage: 'Emphasize the data between an axis and a line.',
|
|
||||||
}),
|
|
||||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush],
|
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
visConfig: {
|
visualization: undefined,
|
||||||
defaults: {
|
|
||||||
type: 'area',
|
|
||||||
grid: {
|
|
||||||
categoryLines: false,
|
|
||||||
},
|
|
||||||
categoryAxes: [
|
|
||||||
{
|
|
||||||
id: 'CategoryAxis-1',
|
|
||||||
type: AxisTypes.CATEGORY,
|
|
||||||
position: Positions.BOTTOM,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
filter: true,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
valueAxes: [
|
|
||||||
{
|
|
||||||
id: 'ValueAxis-1',
|
|
||||||
name: 'LeftAxis-1',
|
|
||||||
type: AxisTypes.VALUE,
|
|
||||||
position: Positions.LEFT,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
mode: AxisModes.NORMAL,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
rotate: Rotates.HORIZONTAL,
|
|
||||||
filter: false,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: countLabel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
seriesParams: [
|
|
||||||
{
|
|
||||||
show: true,
|
|
||||||
type: ChartTypes.AREA,
|
|
||||||
mode: ChartModes.STACKED,
|
|
||||||
data: {
|
|
||||||
label: countLabel,
|
|
||||||
id: '1',
|
|
||||||
},
|
|
||||||
drawLinesBetweenPoints: true,
|
|
||||||
lineWidth: 2,
|
|
||||||
showCircles: true,
|
|
||||||
interpolate: InterpolationModes.LINEAR,
|
|
||||||
valueAxis: 'ValueAxis-1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
addTooltip: true,
|
|
||||||
addLegend: true,
|
|
||||||
legendPosition: Positions.RIGHT,
|
|
||||||
times: [],
|
|
||||||
addTimeMarker: false,
|
|
||||||
thresholdLine: {
|
|
||||||
show: false,
|
|
||||||
value: 10,
|
|
||||||
width: 1,
|
|
||||||
style: ThresholdLineStyles.FULL,
|
|
||||||
color: euiPaletteColorBlind()[9],
|
|
||||||
},
|
|
||||||
labels: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
editorConfig: {
|
|
||||||
collections: getConfigCollections(),
|
|
||||||
optionTabs: getAreaOptionTabs(),
|
|
||||||
schemas: new Schemas([
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'metric',
|
|
||||||
title: i18n.translate('visTypeVislib.area.metricsTitle', {
|
|
||||||
defaultMessage: 'Y-axis',
|
|
||||||
}),
|
|
||||||
aggFilter: ['!geo_centroid', '!geo_bounds'],
|
|
||||||
min: 1,
|
|
||||||
defaults: [{ schema: 'metric', type: 'count' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'radius',
|
|
||||||
title: i18n.translate('visTypeVislib.area.radiusTitle', {
|
|
||||||
defaultMessage: 'Dot size',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'segment',
|
|
||||||
title: i18n.translate('visTypeVislib.area.segmentTitle', {
|
|
||||||
defaultMessage: 'X-axis',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'group',
|
|
||||||
title: i18n.translate('visTypeVislib.area.groupTitle', {
|
|
||||||
defaultMessage: 'Split series',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 3,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'split',
|
|
||||||
title: i18n.translate('visTypeVislib.area.splitTitle', {
|
|
||||||
defaultMessage: 'Split chart',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,248 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { mount, shallow } from 'enzyme';
|
|
||||||
|
|
||||||
import { IAggConfig, IAggType } from 'src/plugins/data/public';
|
|
||||||
import MetricsAxisOptions from './index';
|
|
||||||
import { BasicVislibParams, SeriesParam, ValueAxis } from '../../../types';
|
|
||||||
import { ValidationVisOptionsProps } from '../../common';
|
|
||||||
import { Positions } from '../../../utils/collections';
|
|
||||||
import { ValueAxesPanel } from './value_axes_panel';
|
|
||||||
import { CategoryAxisPanel } from './category_axis_panel';
|
|
||||||
import { ChartTypes } from '../../../utils/collections';
|
|
||||||
import { defaultValueAxisId, valueAxis, seriesParam, categoryAxis } from './mocks';
|
|
||||||
|
|
||||||
jest.mock('./series_panel', () => ({
|
|
||||||
SeriesPanel: () => 'SeriesPanel',
|
|
||||||
}));
|
|
||||||
jest.mock('./category_axis_panel', () => ({
|
|
||||||
CategoryAxisPanel: () => 'CategoryAxisPanel',
|
|
||||||
}));
|
|
||||||
jest.mock('./value_axes_panel', () => ({
|
|
||||||
ValueAxesPanel: () => 'ValueAxesPanel',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SERIES_PARAMS = 'seriesParams';
|
|
||||||
const VALUE_AXES = 'valueAxes';
|
|
||||||
|
|
||||||
const aggCount: IAggConfig = {
|
|
||||||
id: '1',
|
|
||||||
type: { name: 'count' },
|
|
||||||
makeLabel: () => 'Count',
|
|
||||||
} as IAggConfig;
|
|
||||||
|
|
||||||
const aggAverage: IAggConfig = {
|
|
||||||
id: '2',
|
|
||||||
type: { name: 'average' } as IAggType,
|
|
||||||
makeLabel: () => 'Average',
|
|
||||||
} as IAggConfig;
|
|
||||||
|
|
||||||
const createAggs = (aggs: any[]) => ({
|
|
||||||
aggs,
|
|
||||||
bySchemaName: () => aggs,
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('MetricsAxisOptions component', () => {
|
|
||||||
let setValue: jest.Mock;
|
|
||||||
let defaultProps: ValidationVisOptionsProps<BasicVislibParams>;
|
|
||||||
let axis: ValueAxis;
|
|
||||||
let axisRight: ValueAxis;
|
|
||||||
let chart: SeriesParam;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
setValue = jest.fn();
|
|
||||||
|
|
||||||
axis = {
|
|
||||||
...valueAxis,
|
|
||||||
name: 'LeftAxis-1',
|
|
||||||
position: Positions.LEFT,
|
|
||||||
};
|
|
||||||
axisRight = {
|
|
||||||
...valueAxis,
|
|
||||||
id: 'ValueAxis-2',
|
|
||||||
name: 'RightAxis-1',
|
|
||||||
position: Positions.RIGHT,
|
|
||||||
};
|
|
||||||
chart = {
|
|
||||||
...seriesParam,
|
|
||||||
type: ChartTypes.AREA,
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultProps = {
|
|
||||||
aggs: createAggs([aggCount]),
|
|
||||||
isTabSelected: true,
|
|
||||||
vis: {
|
|
||||||
type: {
|
|
||||||
type: ChartTypes.AREA,
|
|
||||||
schemas: { metrics: [{ name: 'metric' }] },
|
|
||||||
},
|
|
||||||
setState: jest.fn(),
|
|
||||||
serialize: jest.fn(),
|
|
||||||
},
|
|
||||||
stateParams: {
|
|
||||||
valueAxes: [axis],
|
|
||||||
seriesParams: [chart],
|
|
||||||
categoryAxes: [categoryAxis],
|
|
||||||
grid: { valueAxis: defaultValueAxisId },
|
|
||||||
},
|
|
||||||
setValue,
|
|
||||||
} as any;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should init with the default set of props', () => {
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
|
|
||||||
expect(comp).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('useEffect', () => {
|
|
||||||
it('should update series when new agg is added', () => {
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([aggCount, aggAverage]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedSeries = [chart, { ...chart, data: { id: '2', label: aggAverage.makeLabel() } }];
|
|
||||||
expect(setValue).toHaveBeenLastCalledWith(SERIES_PARAMS, updatedSeries);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update series when new agg label is changed', () => {
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
const agg = { id: aggCount.id, makeLabel: () => 'New label' };
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([agg]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedSeries = [{ ...chart, data: { id: agg.id, label: agg.makeLabel() } }];
|
|
||||||
expect(setValue).toHaveBeenCalledWith(SERIES_PARAMS, updatedSeries);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('updateAxisTitle', () => {
|
|
||||||
it('should not update the value axis title if custom title was set', () => {
|
|
||||||
defaultProps.stateParams.valueAxes[0].title.text = 'Custom title';
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
const newAgg = {
|
|
||||||
...aggCount,
|
|
||||||
makeLabel: () => 'Custom label',
|
|
||||||
};
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([newAgg]),
|
|
||||||
});
|
|
||||||
const updatedValues = [{ ...axis, title: { text: newAgg.makeLabel() } }];
|
|
||||||
expect(setValue).not.toHaveBeenCalledWith(VALUE_AXES, updatedValues);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the custom title to match the value axis label when only one agg exists for that axis', () => {
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
const agg = {
|
|
||||||
id: aggCount.id,
|
|
||||||
params: { customLabel: 'Custom label' },
|
|
||||||
makeLabel: () => 'Custom label',
|
|
||||||
};
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([agg]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedSeriesParams = [{ ...chart, data: { ...chart.data, label: agg.makeLabel() } }];
|
|
||||||
const updatedValues = [{ ...axis, title: { text: agg.makeLabel() } }];
|
|
||||||
|
|
||||||
expect(setValue).toHaveBeenCalledTimes(5);
|
|
||||||
expect(setValue).toHaveBeenNthCalledWith(3, SERIES_PARAMS, updatedSeriesParams);
|
|
||||||
expect(setValue).toHaveBeenNthCalledWith(5, SERIES_PARAMS, updatedSeriesParams);
|
|
||||||
expect(setValue).toHaveBeenNthCalledWith(4, VALUE_AXES, updatedValues);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not set the custom title to match the value axis label when more than one agg exists for that axis', () => {
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
const agg = { id: aggCount.id, makeLabel: () => 'Custom label' };
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([agg, aggAverage]),
|
|
||||||
stateParams: {
|
|
||||||
...defaultProps.stateParams,
|
|
||||||
seriesParams: [chart, chart],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(setValue).not.toHaveBeenCalledWith(VALUE_AXES);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not overwrite the custom title with the value axis label if the custom title has been changed', () => {
|
|
||||||
defaultProps.stateParams.valueAxes[0].title.text = 'Custom title';
|
|
||||||
const comp = mount(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
const agg = {
|
|
||||||
id: aggCount.id,
|
|
||||||
params: { customLabel: 'Custom label' },
|
|
||||||
makeLabel: () => 'Custom label',
|
|
||||||
};
|
|
||||||
comp.setProps({
|
|
||||||
aggs: createAggs([agg]),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(setValue).not.toHaveBeenCalledWith(VALUE_AXES);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add value axis', () => {
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.find(ValueAxesPanel).prop('addValueAxis')();
|
|
||||||
|
|
||||||
expect(setValue).toHaveBeenCalledWith(VALUE_AXES, [axis, axisRight]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('removeValueAxis', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
defaultProps.stateParams.valueAxes = [axis, axisRight];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove value axis', () => {
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.find(ValueAxesPanel).prop('removeValueAxis')(axis);
|
|
||||||
|
|
||||||
expect(setValue).toHaveBeenCalledWith(VALUE_AXES, [axisRight]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update seriesParams "valueAxis" prop', () => {
|
|
||||||
const updatedSeriesParam = { ...chart, valueAxis: 'ValueAxis-2' };
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.find(ValueAxesPanel).prop('removeValueAxis')(axis);
|
|
||||||
|
|
||||||
expect(setValue).toHaveBeenCalledWith(SERIES_PARAMS, [updatedSeriesParam]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset grid "valueAxis" prop', () => {
|
|
||||||
const updatedGrid = { valueAxis: undefined };
|
|
||||||
defaultProps.stateParams.seriesParams[0].valueAxis = 'ValueAxis-2';
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.find(ValueAxesPanel).prop('removeValueAxis')(axis);
|
|
||||||
|
|
||||||
expect(setValue).toHaveBeenCalledWith('grid', updatedGrid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update axis value when when category position chnaged', () => {
|
|
||||||
const comp = shallow(<MetricsAxisOptions {...defaultProps} />);
|
|
||||||
comp.find(CategoryAxisPanel).prop('onPositionChanged')(Positions.LEFT);
|
|
||||||
|
|
||||||
const updatedValues = [{ ...axis, name: 'BottomAxis-1', position: Positions.BOTTOM }];
|
|
||||||
expect(setValue).toHaveBeenCalledWith(VALUE_AXES, updatedValues);
|
|
||||||
});
|
|
||||||
});
|
|
73
src/plugins/vis_type_vislib/public/editor/collections.ts
Normal file
73
src/plugins/vis_type_vislib/public/editor/collections.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import { colorSchemas } from '../../../charts/public';
|
||||||
|
import { getPositions, getScaleTypes } from '../../../vis_type_xy/public';
|
||||||
|
|
||||||
|
import { Alignment, GaugeType } from '../types';
|
||||||
|
|
||||||
|
export const getGaugeTypes = () => [
|
||||||
|
{
|
||||||
|
text: i18n.translate('visTypeVislib.gauge.gaugeTypes.arcText', {
|
||||||
|
defaultMessage: 'Arc',
|
||||||
|
}),
|
||||||
|
value: GaugeType.Arc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.translate('visTypeVislib.gauge.gaugeTypes.circleText', {
|
||||||
|
defaultMessage: 'Circle',
|
||||||
|
}),
|
||||||
|
value: GaugeType.Circle,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getAlignments = () => [
|
||||||
|
{
|
||||||
|
text: i18n.translate('visTypeVislib.gauge.alignmentAutomaticTitle', {
|
||||||
|
defaultMessage: 'Automatic',
|
||||||
|
}),
|
||||||
|
value: Alignment.Automatic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.translate('visTypeVislib.gauge.alignmentHorizontalTitle', {
|
||||||
|
defaultMessage: 'Horizontal',
|
||||||
|
}),
|
||||||
|
value: Alignment.Horizontal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.translate('visTypeVislib.gauge.alignmentVerticalTitle', {
|
||||||
|
defaultMessage: 'Vertical',
|
||||||
|
}),
|
||||||
|
value: Alignment.Vertical,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getGaugeCollections = () => ({
|
||||||
|
gaugeTypes: getGaugeTypes(),
|
||||||
|
alignments: getAlignments(),
|
||||||
|
colorSchemas,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getHeatmapCollections = () => ({
|
||||||
|
legendPositions: getPositions(),
|
||||||
|
scales: getScaleTypes(),
|
||||||
|
colorSchemas,
|
||||||
|
});
|
|
@ -23,7 +23,8 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
|
||||||
|
import { ValueAxis } from '../../../../../vis_type_xy/public';
|
||||||
import {
|
import {
|
||||||
BasicOptions,
|
BasicOptions,
|
||||||
ColorRanges,
|
ColorRanges,
|
||||||
|
@ -34,8 +35,8 @@ import {
|
||||||
SetColorSchemaOptionsValue,
|
SetColorSchemaOptionsValue,
|
||||||
SetColorRangeValue,
|
SetColorRangeValue,
|
||||||
} from '../../../../../charts/public';
|
} from '../../../../../charts/public';
|
||||||
|
|
||||||
import { HeatmapVisParams } from '../../../heatmap';
|
import { HeatmapVisParams } from '../../../heatmap';
|
||||||
import { ValueAxis } from '../../../types';
|
|
||||||
import { LabelsPanel } from './labels_panel';
|
import { LabelsPanel } from './labels_panel';
|
||||||
|
|
||||||
function HeatmapOptions(props: VisOptionsProps<HeatmapVisParams>) {
|
function HeatmapOptions(props: VisOptionsProps<HeatmapVisParams>) {
|
|
@ -23,10 +23,11 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
|
||||||
import { ValueAxis } from '../../../types';
|
|
||||||
import { HeatmapVisParams } from '../../../heatmap';
|
|
||||||
import { SwitchOption } from '../../../../../charts/public';
|
import { SwitchOption } from '../../../../../charts/public';
|
||||||
|
import { ValueAxis } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
|
import { HeatmapVisParams } from '../../../heatmap';
|
||||||
|
|
||||||
const VERTICAL_ROTATION = 270;
|
const VERTICAL_ROTATION = 270;
|
||||||
|
|
|
@ -19,18 +19,15 @@
|
||||||
|
|
||||||
import React, { lazy } from 'react';
|
import React, { lazy } from 'react';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from '../../../../vis_default_editor/public';
|
||||||
import { ValidationVisOptionsProps } from '../common';
|
|
||||||
import { GaugeVisParams } from '../../gauge';
|
import { GaugeVisParams } from '../../gauge';
|
||||||
import { PieVisParams } from '../../pie';
|
import { PieVisParams } from '../../pie';
|
||||||
import { BasicVislibParams } from '../../types';
|
|
||||||
import { HeatmapVisParams } from '../../heatmap';
|
import { HeatmapVisParams } from '../../heatmap';
|
||||||
|
|
||||||
const GaugeOptionsLazy = lazy(() => import('./gauge'));
|
const GaugeOptionsLazy = lazy(() => import('./gauge'));
|
||||||
const PieOptionsLazy = lazy(() => import('./pie'));
|
const PieOptionsLazy = lazy(() => import('./pie'));
|
||||||
const PointSeriesOptionsLazy = lazy(() => import('./point_series'));
|
|
||||||
const HeatmapOptionsLazy = lazy(() => import('./heatmap'));
|
const HeatmapOptionsLazy = lazy(() => import('./heatmap'));
|
||||||
const MetricsAxisOptionsLazy = lazy(() => import('./metrics_axes'));
|
|
||||||
|
|
||||||
export const GaugeOptions = (props: VisOptionsProps<GaugeVisParams>) => (
|
export const GaugeOptions = (props: VisOptionsProps<GaugeVisParams>) => (
|
||||||
<GaugeOptionsLazy {...props} />
|
<GaugeOptionsLazy {...props} />
|
||||||
|
@ -38,14 +35,6 @@ export const GaugeOptions = (props: VisOptionsProps<GaugeVisParams>) => (
|
||||||
|
|
||||||
export const PieOptions = (props: VisOptionsProps<PieVisParams>) => <PieOptionsLazy {...props} />;
|
export const PieOptions = (props: VisOptionsProps<PieVisParams>) => <PieOptionsLazy {...props} />;
|
||||||
|
|
||||||
export const PointSeriesOptions = (props: ValidationVisOptionsProps<BasicVislibParams>) => (
|
|
||||||
<PointSeriesOptionsLazy {...props} />
|
|
||||||
);
|
|
||||||
|
|
||||||
export const HeatmapOptions = (props: VisOptionsProps<HeatmapVisParams>) => (
|
export const HeatmapOptions = (props: VisOptionsProps<HeatmapVisParams>) => (
|
||||||
<HeatmapOptionsLazy {...props} />
|
<HeatmapOptionsLazy {...props} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const MetricsAxisOptions = (props: ValidationVisOptionsProps<BasicVislibParams>) => (
|
|
||||||
<MetricsAxisOptionsLazy {...props} />
|
|
||||||
);
|
|
|
@ -22,9 +22,10 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
import { VisOptionsProps } from '../../../../vis_default_editor/public';
|
||||||
import { TruncateLabelsOption } from '../common';
|
|
||||||
import { BasicOptions, SwitchOption } from '../../../../charts/public';
|
import { BasicOptions, SwitchOption } from '../../../../charts/public';
|
||||||
|
import { TruncateLabelsOption } from '../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { PieVisParams } from '../../pie';
|
import { PieVisParams } from '../../pie';
|
||||||
|
|
||||||
function PieOptions(props: VisOptionsProps<PieVisParams>) {
|
function PieOptions(props: VisOptionsProps<PieVisParams>) {
|
21
src/plugins/vis_type_vislib/public/editor/index.ts
Normal file
21
src/plugins/vis_type_vislib/public/editor/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './collections';
|
||||||
|
export * from './components';
|
|
@ -19,24 +19,25 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import { ColorMode, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public';
|
||||||
import { RangeValues, Schemas } from '../../vis_default_editor/public';
|
import { RangeValues, Schemas } from '../../vis_default_editor/public';
|
||||||
import { AggGroupNames } from '../../data/public';
|
import { AggGroupNames } from '../../data/public';
|
||||||
import { GaugeOptions } from './components/options';
|
|
||||||
import { getGaugeCollections, Alignments, GaugeTypes } from './utils/collections';
|
|
||||||
import { ColorModes, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public';
|
|
||||||
import { toExpressionAst } from './to_ast';
|
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
|
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
|
||||||
import { BasicVislibParams } from './types';
|
|
||||||
|
import { Alignment, GaugeType, BasicVislibParams, VislibChartType } from './types';
|
||||||
|
import { getGaugeCollections } from './editor';
|
||||||
|
import { toExpressionAst } from './to_ast';
|
||||||
|
import { GaugeOptions } from './editor/components';
|
||||||
|
|
||||||
export interface Gauge extends ColorSchemaParams {
|
export interface Gauge extends ColorSchemaParams {
|
||||||
backStyle: 'Full';
|
backStyle: 'Full';
|
||||||
gaugeStyle: 'Full';
|
gaugeStyle: 'Full';
|
||||||
orientation: 'vertical';
|
orientation: 'vertical';
|
||||||
type: 'meter';
|
type: 'meter';
|
||||||
alignment: Alignments;
|
alignment: Alignment;
|
||||||
colorsRange: RangeValues[];
|
colorsRange: RangeValues[];
|
||||||
extendRange: boolean;
|
extendRange: boolean;
|
||||||
gaugeType: GaugeTypes;
|
gaugeType: GaugeType;
|
||||||
labels: Labels;
|
labels: Labels;
|
||||||
percentageMode: boolean;
|
percentageMode: boolean;
|
||||||
outline?: boolean;
|
outline?: boolean;
|
||||||
|
@ -67,20 +68,20 @@ export const gaugeVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
visConfig: {
|
visConfig: {
|
||||||
defaults: {
|
defaults: {
|
||||||
type: 'gauge',
|
type: VislibChartType.Gauge,
|
||||||
addTooltip: true,
|
addTooltip: true,
|
||||||
addLegend: true,
|
addLegend: true,
|
||||||
isDisplayWarning: false,
|
isDisplayWarning: false,
|
||||||
gauge: {
|
gauge: {
|
||||||
alignment: Alignments.AUTOMATIC,
|
alignment: Alignment.Automatic,
|
||||||
extendRange: true,
|
extendRange: true,
|
||||||
percentageMode: false,
|
percentageMode: false,
|
||||||
gaugeType: GaugeTypes.ARC,
|
gaugeType: GaugeType.Arc,
|
||||||
gaugeStyle: 'Full',
|
gaugeStyle: 'Full',
|
||||||
backStyle: 'Full',
|
backStyle: 'Full',
|
||||||
orientation: 'vertical',
|
orientation: 'vertical',
|
||||||
colorSchema: ColorSchemas.GreenToRed,
|
colorSchema: ColorSchemas.GreenToRed,
|
||||||
gaugeColorMode: ColorModes.LABELS,
|
gaugeColorMode: ColorMode.Labels,
|
||||||
colorsRange: [
|
colorsRange: [
|
||||||
{ from: 0, to: 50 },
|
{ from: 0, to: 50 },
|
||||||
{ from: 50, to: 75 },
|
{ from: 50, to: 75 },
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { GaugeOptions } from './components/options';
|
|
||||||
import { getGaugeCollections, GaugeTypes } from './utils/collections';
|
|
||||||
import { ColorModes, ColorSchemas } from '../../charts/public';
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
import { AggGroupNames } from '../../data/public';
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
import { Schemas } from '../../vis_default_editor/public';
|
||||||
import { toExpressionAst } from './to_ast';
|
import { ColorMode, ColorSchemas } from '../../charts/public';
|
||||||
import { BaseVisTypeOptions } from '../../visualizations/public';
|
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
import { BasicVislibParams } from './types';
|
|
||||||
|
import { getGaugeCollections, GaugeOptions } from './editor';
|
||||||
|
import { toExpressionAst } from './to_ast';
|
||||||
|
import { GaugeType, BasicVislibParams } from './types';
|
||||||
|
|
||||||
export const goalVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
export const goalVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
name: 'goal',
|
name: 'goal',
|
||||||
|
@ -46,13 +46,13 @@ export const goalVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
verticalSplit: false,
|
verticalSplit: false,
|
||||||
autoExtend: false,
|
autoExtend: false,
|
||||||
percentageMode: true,
|
percentageMode: true,
|
||||||
gaugeType: GaugeTypes.ARC,
|
gaugeType: GaugeType.Arc,
|
||||||
gaugeStyle: 'Full',
|
gaugeStyle: 'Full',
|
||||||
backStyle: 'Full',
|
backStyle: 'Full',
|
||||||
orientation: 'vertical',
|
orientation: 'vertical',
|
||||||
useRanges: false,
|
useRanges: false,
|
||||||
colorSchema: ColorSchemas.GreenToRed,
|
colorSchema: ColorSchemas.GreenToRed,
|
||||||
gaugeColorMode: ColorModes.NONE,
|
gaugeColorMode: ColorMode.None,
|
||||||
colorsRange: [{ from: 0, to: 10000 }],
|
colorsRange: [{ from: 0, to: 10000 }],
|
||||||
invertColors: false,
|
invertColors: false,
|
||||||
labels: {
|
labels: {
|
||||||
|
|
|
@ -18,15 +18,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { Position } from '@elastic/charts';
|
||||||
|
|
||||||
import { RangeValues, Schemas } from '../../vis_default_editor/public';
|
import { RangeValues, Schemas } from '../../vis_default_editor/public';
|
||||||
import { AggGroupNames } from '../../data/public';
|
import { AggGroupNames } from '../../data/public';
|
||||||
import { AxisTypes, getHeatmapCollections, Positions, ScaleTypes } from './utils/collections';
|
|
||||||
import { HeatmapOptions } from './components/options';
|
|
||||||
import { TimeMarker } from './vislib/visualizations/time_marker';
|
|
||||||
import { BasicVislibParams, CommonVislibParams, ValueAxis } from './types';
|
|
||||||
import { ColorSchemas, ColorSchemaParams } from '../../charts/public';
|
import { ColorSchemas, ColorSchemaParams } from '../../charts/public';
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
import { VIS_EVENT_TO_TRIGGER, BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
|
import { ValueAxis, ScaleType, AxisType } from '../../vis_type_xy/public';
|
||||||
|
|
||||||
|
import { HeatmapOptions, getHeatmapCollections } from './editor';
|
||||||
|
import { TimeMarker } from './vislib/visualizations/time_marker';
|
||||||
|
import { CommonVislibParams, BasicVislibParams, VislibChartType } from './types';
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
|
|
||||||
export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams {
|
export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams {
|
||||||
|
@ -48,15 +50,15 @@ export const heatmapVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
description: i18n.translate('visTypeVislib.heatmap.heatmapDescription', {
|
description: i18n.translate('visTypeVislib.heatmap.heatmapDescription', {
|
||||||
defaultMessage: 'Shade data in cells in a matrix.',
|
defaultMessage: 'Shade data in cells in a matrix.',
|
||||||
}),
|
}),
|
||||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
|
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
|
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
|
||||||
visConfig: {
|
visConfig: {
|
||||||
defaults: {
|
defaults: {
|
||||||
type: 'heatmap',
|
type: VislibChartType.Heatmap,
|
||||||
addTooltip: true,
|
addTooltip: true,
|
||||||
addLegend: true,
|
addLegend: true,
|
||||||
enableHover: false,
|
enableHover: false,
|
||||||
legendPosition: Positions.RIGHT,
|
legendPosition: Position.Right,
|
||||||
times: [],
|
times: [],
|
||||||
colorsNumber: 4,
|
colorsNumber: 4,
|
||||||
colorSchema: ColorSchemas.Greens,
|
colorSchema: ColorSchemas.Greens,
|
||||||
|
@ -68,9 +70,9 @@ export const heatmapVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
{
|
{
|
||||||
show: false,
|
show: false,
|
||||||
id: 'ValueAxis-1',
|
id: 'ValueAxis-1',
|
||||||
type: AxisTypes.VALUE,
|
type: AxisType.Value,
|
||||||
scale: {
|
scale: {
|
||||||
type: ScaleTypes.LINEAR,
|
type: ScaleType.Linear,
|
||||||
defaultYExtents: false,
|
defaultYExtents: false,
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
|
|
|
@ -17,174 +17,14 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||||
// @ts-ignore
|
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
import { palettes } from '@elastic/eui/lib/services';
|
|
||||||
// @ts-ignore
|
|
||||||
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
|
||||||
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
|
||||||
import {
|
|
||||||
Positions,
|
|
||||||
ChartTypes,
|
|
||||||
ChartModes,
|
|
||||||
AxisTypes,
|
|
||||||
ScaleTypes,
|
|
||||||
AxisModes,
|
|
||||||
ThresholdLineStyles,
|
|
||||||
getConfigCollections,
|
|
||||||
} from './utils/collections';
|
|
||||||
import { getAreaOptionTabs, countLabel } from './utils/common_config';
|
|
||||||
import { Rotates } from '../../charts/public';
|
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
|
||||||
import { BasicVislibParams } from './types';
|
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
|
import { BasicVislibParams } from './types';
|
||||||
|
|
||||||
export const histogramVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
export const histogramVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
name: 'histogram',
|
...(xyVisTypes.histogram() as BaseVisTypeOptions<BasicVislibParams>),
|
||||||
title: i18n.translate('visTypeVislib.histogram.histogramTitle', {
|
|
||||||
defaultMessage: 'Vertical bar',
|
|
||||||
}),
|
|
||||||
icon: 'visBarVertical',
|
|
||||||
description: i18n.translate('visTypeVislib.histogram.histogramDescription', {
|
|
||||||
defaultMessage: 'Present data in vertical bars on an axis.',
|
|
||||||
}),
|
|
||||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush],
|
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
visConfig: {
|
visualization: undefined,
|
||||||
defaults: {
|
|
||||||
type: 'histogram',
|
|
||||||
grid: {
|
|
||||||
categoryLines: false,
|
|
||||||
},
|
|
||||||
categoryAxes: [
|
|
||||||
{
|
|
||||||
id: 'CategoryAxis-1',
|
|
||||||
type: AxisTypes.CATEGORY,
|
|
||||||
position: Positions.BOTTOM,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
filter: true,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
valueAxes: [
|
|
||||||
{
|
|
||||||
id: 'ValueAxis-1',
|
|
||||||
name: 'LeftAxis-1',
|
|
||||||
type: AxisTypes.VALUE,
|
|
||||||
position: Positions.LEFT,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
mode: AxisModes.NORMAL,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
rotate: Rotates.HORIZONTAL,
|
|
||||||
filter: false,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: countLabel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
seriesParams: [
|
|
||||||
{
|
|
||||||
show: true,
|
|
||||||
type: ChartTypes.HISTOGRAM,
|
|
||||||
mode: ChartModes.STACKED,
|
|
||||||
data: {
|
|
||||||
label: countLabel,
|
|
||||||
id: '1',
|
|
||||||
},
|
|
||||||
valueAxis: 'ValueAxis-1',
|
|
||||||
drawLinesBetweenPoints: true,
|
|
||||||
lineWidth: 2,
|
|
||||||
showCircles: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
addTooltip: true,
|
|
||||||
addLegend: true,
|
|
||||||
legendPosition: Positions.RIGHT,
|
|
||||||
times: [],
|
|
||||||
addTimeMarker: false,
|
|
||||||
labels: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
thresholdLine: {
|
|
||||||
show: false,
|
|
||||||
value: 10,
|
|
||||||
width: 1,
|
|
||||||
style: ThresholdLineStyles.FULL,
|
|
||||||
color: euiPaletteColorBlind()[9],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
editorConfig: {
|
|
||||||
collections: getConfigCollections(),
|
|
||||||
optionTabs: getAreaOptionTabs(),
|
|
||||||
schemas: new Schemas([
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'metric',
|
|
||||||
title: i18n.translate('visTypeVislib.histogram.metricTitle', {
|
|
||||||
defaultMessage: 'Y-axis',
|
|
||||||
}),
|
|
||||||
min: 1,
|
|
||||||
aggFilter: ['!geo_centroid', '!geo_bounds'],
|
|
||||||
defaults: [{ schema: 'metric', type: 'count' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'radius',
|
|
||||||
title: i18n.translate('visTypeVislib.histogram.radiusTitle', {
|
|
||||||
defaultMessage: 'Dot size',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'segment',
|
|
||||||
title: i18n.translate('visTypeVislib.histogram.segmentTitle', {
|
|
||||||
defaultMessage: 'X-axis',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'group',
|
|
||||||
title: i18n.translate('visTypeVislib.histogram.groupTitle', {
|
|
||||||
defaultMessage: 'Split series',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 3,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'split',
|
|
||||||
title: i18n.translate('visTypeVislib.histogram.splitTitle', {
|
|
||||||
defaultMessage: 'Split chart',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,171 +17,14 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||||
// @ts-ignore
|
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
|
||||||
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
|
||||||
import {
|
|
||||||
Positions,
|
|
||||||
ChartTypes,
|
|
||||||
ChartModes,
|
|
||||||
AxisTypes,
|
|
||||||
ScaleTypes,
|
|
||||||
AxisModes,
|
|
||||||
ThresholdLineStyles,
|
|
||||||
getConfigCollections,
|
|
||||||
} from './utils/collections';
|
|
||||||
import { getAreaOptionTabs, countLabel } from './utils/common_config';
|
|
||||||
import { Rotates } from '../../charts/public';
|
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
|
||||||
import { BasicVislibParams } from './types';
|
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
|
import { BasicVislibParams } from './types';
|
||||||
|
|
||||||
export const horizontalBarVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
export const horizontalBarVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
name: 'horizontal_bar',
|
...(xyVisTypes.horizontalBar() as BaseVisTypeOptions<BasicVislibParams>),
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.horizontalBarTitle', {
|
|
||||||
defaultMessage: 'Horizontal bar',
|
|
||||||
}),
|
|
||||||
icon: 'visBarHorizontal',
|
|
||||||
description: i18n.translate('visTypeVislib.horizontalBar.horizontalBarDescription', {
|
|
||||||
defaultMessage: 'Present data in horizontal bars on an axis.',
|
|
||||||
}),
|
|
||||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush],
|
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
visConfig: {
|
visualization: undefined,
|
||||||
defaults: {
|
|
||||||
type: 'histogram',
|
|
||||||
grid: {
|
|
||||||
categoryLines: false,
|
|
||||||
},
|
|
||||||
categoryAxes: [
|
|
||||||
{
|
|
||||||
id: 'CategoryAxis-1',
|
|
||||||
type: AxisTypes.CATEGORY,
|
|
||||||
position: Positions.LEFT,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
rotate: Rotates.HORIZONTAL,
|
|
||||||
filter: false,
|
|
||||||
truncate: 200,
|
|
||||||
},
|
|
||||||
title: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
valueAxes: [
|
|
||||||
{
|
|
||||||
id: 'ValueAxis-1',
|
|
||||||
name: 'LeftAxis-1',
|
|
||||||
type: AxisTypes.VALUE,
|
|
||||||
position: Positions.BOTTOM,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
mode: AxisModes.NORMAL,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
rotate: Rotates.ANGLED,
|
|
||||||
filter: true,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: countLabel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
seriesParams: [
|
|
||||||
{
|
|
||||||
show: true,
|
|
||||||
type: ChartTypes.HISTOGRAM,
|
|
||||||
mode: ChartModes.NORMAL,
|
|
||||||
data: {
|
|
||||||
label: countLabel,
|
|
||||||
id: '1',
|
|
||||||
},
|
|
||||||
valueAxis: 'ValueAxis-1',
|
|
||||||
drawLinesBetweenPoints: true,
|
|
||||||
lineWidth: 2,
|
|
||||||
showCircles: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
addTooltip: true,
|
|
||||||
addLegend: true,
|
|
||||||
legendPosition: Positions.RIGHT,
|
|
||||||
times: [],
|
|
||||||
addTimeMarker: false,
|
|
||||||
labels: {},
|
|
||||||
thresholdLine: {
|
|
||||||
show: false,
|
|
||||||
value: 10,
|
|
||||||
width: 1,
|
|
||||||
style: ThresholdLineStyles.FULL,
|
|
||||||
color: euiPaletteColorBlind()[9],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
editorConfig: {
|
|
||||||
collections: getConfigCollections(),
|
|
||||||
optionTabs: getAreaOptionTabs(),
|
|
||||||
schemas: new Schemas([
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'metric',
|
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.metricTitle', {
|
|
||||||
defaultMessage: 'Y-axis',
|
|
||||||
}),
|
|
||||||
min: 1,
|
|
||||||
aggFilter: ['!geo_centroid', '!geo_bounds'],
|
|
||||||
defaults: [{ schema: 'metric', type: 'count' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'radius',
|
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.radiusTitle', {
|
|
||||||
defaultMessage: 'Dot size',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'segment',
|
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.segmentTitle', {
|
|
||||||
defaultMessage: 'X-axis',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'group',
|
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.groupTitle', {
|
|
||||||
defaultMessage: 'Split series',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 3,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'split',
|
|
||||||
title: i18n.translate('visTypeVislib.horizontalBar.splitTitle', {
|
|
||||||
defaultMessage: 'Split chart',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,164 +17,14 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||||
// @ts-ignore
|
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||||
import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
|
||||||
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
|
||||||
import {
|
|
||||||
Positions,
|
|
||||||
ChartTypes,
|
|
||||||
ChartModes,
|
|
||||||
AxisTypes,
|
|
||||||
ScaleTypes,
|
|
||||||
AxisModes,
|
|
||||||
ThresholdLineStyles,
|
|
||||||
InterpolationModes,
|
|
||||||
getConfigCollections,
|
|
||||||
} from './utils/collections';
|
|
||||||
import { getAreaOptionTabs, countLabel } from './utils/common_config';
|
|
||||||
import { Rotates } from '../../charts/public';
|
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
|
||||||
import { toExpressionAst } from './to_ast';
|
import { toExpressionAst } from './to_ast';
|
||||||
import { BasicVislibParams } from './types';
|
import { BasicVislibParams } from './types';
|
||||||
|
|
||||||
export const lineVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
export const lineVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||||
name: 'line',
|
...(xyVisTypes.line() as BaseVisTypeOptions<BasicVislibParams>),
|
||||||
title: i18n.translate('visTypeVislib.line.lineTitle', { defaultMessage: 'Line' }),
|
|
||||||
icon: 'visLine',
|
|
||||||
description: i18n.translate('visTypeVislib.line.lineDescription', {
|
|
||||||
defaultMessage: 'Display data as a series of points.',
|
|
||||||
}),
|
|
||||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush],
|
|
||||||
toExpressionAst,
|
toExpressionAst,
|
||||||
visConfig: {
|
visualization: undefined,
|
||||||
defaults: {
|
|
||||||
type: 'line',
|
|
||||||
grid: {
|
|
||||||
categoryLines: false,
|
|
||||||
},
|
|
||||||
categoryAxes: [
|
|
||||||
{
|
|
||||||
id: 'CategoryAxis-1',
|
|
||||||
type: AxisTypes.CATEGORY,
|
|
||||||
position: Positions.BOTTOM,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
filter: true,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
valueAxes: [
|
|
||||||
{
|
|
||||||
id: 'ValueAxis-1',
|
|
||||||
name: 'LeftAxis-1',
|
|
||||||
type: AxisTypes.VALUE,
|
|
||||||
position: Positions.LEFT,
|
|
||||||
show: true,
|
|
||||||
style: {},
|
|
||||||
scale: {
|
|
||||||
type: ScaleTypes.LINEAR,
|
|
||||||
mode: AxisModes.NORMAL,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: true,
|
|
||||||
rotate: Rotates.HORIZONTAL,
|
|
||||||
filter: false,
|
|
||||||
truncate: 100,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: countLabel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
seriesParams: [
|
|
||||||
{
|
|
||||||
show: true,
|
|
||||||
type: ChartTypes.LINE,
|
|
||||||
mode: ChartModes.NORMAL,
|
|
||||||
data: {
|
|
||||||
label: countLabel,
|
|
||||||
id: '1',
|
|
||||||
},
|
|
||||||
valueAxis: 'ValueAxis-1',
|
|
||||||
drawLinesBetweenPoints: true,
|
|
||||||
lineWidth: 2,
|
|
||||||
interpolate: InterpolationModes.LINEAR,
|
|
||||||
showCircles: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
addTooltip: true,
|
|
||||||
addLegend: true,
|
|
||||||
legendPosition: Positions.RIGHT,
|
|
||||||
times: [],
|
|
||||||
addTimeMarker: false,
|
|
||||||
labels: {},
|
|
||||||
thresholdLine: {
|
|
||||||
show: false,
|
|
||||||
value: 10,
|
|
||||||
width: 1,
|
|
||||||
style: ThresholdLineStyles.FULL,
|
|
||||||
color: euiPaletteColorBlind()[9],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
editorConfig: {
|
|
||||||
collections: getConfigCollections(),
|
|
||||||
optionTabs: getAreaOptionTabs(),
|
|
||||||
schemas: new Schemas([
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'metric',
|
|
||||||
title: i18n.translate('visTypeVislib.line.metricTitle', { defaultMessage: 'Y-axis' }),
|
|
||||||
min: 1,
|
|
||||||
aggFilter: ['!geo_centroid', '!geo_bounds'],
|
|
||||||
defaults: [{ schema: 'metric', type: 'count' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Metrics,
|
|
||||||
name: 'radius',
|
|
||||||
title: i18n.translate('visTypeVislib.line.radiusTitle', { defaultMessage: 'Dot size' }),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality', 'top_hits'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'segment',
|
|
||||||
title: i18n.translate('visTypeVislib.line.segmentTitle', { defaultMessage: 'X-axis' }),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'group',
|
|
||||||
title: i18n.translate('visTypeVislib.line.groupTitle', {
|
|
||||||
defaultMessage: 'Split series',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 3,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: AggGroupNames.Buckets,
|
|
||||||
name: 'split',
|
|
||||||
title: i18n.translate('visTypeVislib.line.splitTitle', {
|
|
||||||
defaultMessage: 'Split chart',
|
|
||||||
}),
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,13 +18,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { Position } from '@elastic/charts';
|
||||||
|
|
||||||
import { AggGroupNames } from '../../data/public';
|
import { AggGroupNames } from '../../data/public';
|
||||||
import { Schemas } from '../../vis_default_editor/public';
|
import { Schemas } from '../../vis_default_editor/public';
|
||||||
import { PieOptions } from './components/options';
|
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
|
||||||
import { getPositions, Positions } from './utils/collections';
|
import { getPositions } from '../../vis_type_xy/public';
|
||||||
|
|
||||||
import { CommonVislibParams } from './types';
|
import { CommonVislibParams } from './types';
|
||||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
import { PieOptions } from './editor';
|
||||||
import { toExpressionAst } from './to_ast_pie';
|
import { toExpressionAst } from './to_ast_pie';
|
||||||
|
|
||||||
export interface PieVisParams extends CommonVislibParams {
|
export interface PieVisParams extends CommonVislibParams {
|
||||||
|
@ -52,7 +54,7 @@ export const pieVisTypeDefinition: BaseVisTypeOptions<PieVisParams> = {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
addTooltip: true,
|
addTooltip: true,
|
||||||
addLegend: true,
|
addLegend: true,
|
||||||
legendPosition: Positions.RIGHT,
|
legendPosition: Position.Right,
|
||||||
isDonut: true,
|
isDonut: true,
|
||||||
labels: {
|
labels: {
|
||||||
show: false,
|
show: false,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressio
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { vislibSlicesResponseHandler } from './vislib/response_handler';
|
import { vislibSlicesResponseHandler } from './vislib/response_handler';
|
||||||
import { PieVisParams } from './pie';
|
import { PieVisParams } from './pie';
|
||||||
|
import { VislibChartType } from './types';
|
||||||
import { vislibVisName } from './vis_type_vislib_vis_fn';
|
import { vislibVisName } from './vis_type_vislib_vis_fn';
|
||||||
|
|
||||||
export const vislibPieName = 'vislib_pie_vis';
|
export const vislibPieName = 'vislib_pie_vis';
|
||||||
|
@ -32,9 +33,9 @@ interface Arguments {
|
||||||
visConfig: string;
|
visConfig: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RenderValue {
|
export interface PieRenderValue {
|
||||||
|
visType: Extract<VislibChartType, 'pie'>;
|
||||||
visData: unknown;
|
visData: unknown;
|
||||||
visType: string;
|
|
||||||
visConfig: PieVisParams;
|
visConfig: PieVisParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ export type VisTypeVislibPieExpressionFunctionDefinition = ExpressionFunctionDef
|
||||||
typeof vislibPieName,
|
typeof vislibPieName,
|
||||||
Datatable,
|
Datatable,
|
||||||
Arguments,
|
Arguments,
|
||||||
Render<RenderValue>
|
Render<PieRenderValue>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const createPieVisFn = (): VisTypeVislibPieExpressionFunctionDefinition => ({
|
export const createPieVisFn = (): VisTypeVislibPieExpressionFunctionDefinition => ({
|
||||||
|
@ -73,7 +74,7 @@ export const createPieVisFn = (): VisTypeVislibPieExpressionFunctionDefinition =
|
||||||
value: {
|
value: {
|
||||||
visData,
|
visData,
|
||||||
visConfig,
|
visConfig,
|
||||||
visType: 'pie',
|
visType: VislibChartType.Pie,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,25 +19,28 @@
|
||||||
|
|
||||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
|
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
|
||||||
|
|
||||||
import { VisTypeXyPluginSetup } from 'src/plugins/vis_type_xy/public';
|
|
||||||
import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
|
import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
|
||||||
import { BaseVisTypeOptions, VisualizationsSetup } from '../../visualizations/public';
|
import { VisualizationsSetup } from '../../visualizations/public';
|
||||||
import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn';
|
|
||||||
import { createPieVisFn } from './pie_fn';
|
|
||||||
import { visLibVisTypeDefinitions, pieVisTypeDefinition } from './vis_type_vislib_vis_types';
|
|
||||||
import { ChartsPluginSetup } from '../../charts/public';
|
import { ChartsPluginSetup } from '../../charts/public';
|
||||||
import { DataPublicPluginStart } from '../../data/public';
|
import { DataPublicPluginStart } from '../../data/public';
|
||||||
import { KibanaLegacyStart } from '../../kibana_legacy/public';
|
import { KibanaLegacyStart } from '../../kibana_legacy/public';
|
||||||
|
import { CHARTS_LIBRARY } from '../../vis_type_xy/public';
|
||||||
|
|
||||||
|
import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn';
|
||||||
|
import { createPieVisFn } from './pie_fn';
|
||||||
|
import {
|
||||||
|
convertedTypeDefinitions,
|
||||||
|
pieVisTypeDefinition,
|
||||||
|
visLibVisTypeDefinitions,
|
||||||
|
} from './vis_type_vislib_vis_types';
|
||||||
import { setFormatService, setDataActions } from './services';
|
import { setFormatService, setDataActions } from './services';
|
||||||
import { getVislibVisRenderer } from './vis_renderer';
|
import { getVislibVisRenderer } from './vis_renderer';
|
||||||
import { BasicVislibParams } from './types';
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export interface VisTypeVislibPluginSetupDependencies {
|
export interface VisTypeVislibPluginSetupDependencies {
|
||||||
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
|
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
|
||||||
visualizations: VisualizationsSetup;
|
visualizations: VisualizationsSetup;
|
||||||
charts: ChartsPluginSetup;
|
charts: ChartsPluginSetup;
|
||||||
visTypeXy?: VisTypeXyPluginSetup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -56,23 +59,21 @@ export class VisTypeVislibPlugin
|
||||||
|
|
||||||
public async setup(
|
public async setup(
|
||||||
core: VisTypeVislibCoreSetup,
|
core: VisTypeVislibCoreSetup,
|
||||||
{ expressions, visualizations, charts, visTypeXy }: VisTypeVislibPluginSetupDependencies
|
{ expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies
|
||||||
) {
|
) {
|
||||||
// if visTypeXy plugin is disabled it's config will be undefined
|
if (core.uiSettings.get(CHARTS_LIBRARY)) {
|
||||||
if (!visTypeXy) {
|
// Register only non-replaced vis types
|
||||||
const convertedTypes: Array<BaseVisTypeOptions<BasicVislibParams>> = [];
|
convertedTypeDefinitions.forEach(visualizations.createBaseVisualization);
|
||||||
const convertedFns: any[] = [];
|
visualizations.createBaseVisualization(pieVisTypeDefinition);
|
||||||
|
|
||||||
// Register legacy vislib types that have been converted
|
|
||||||
convertedFns.forEach(expressions.registerFunction);
|
|
||||||
convertedTypes.forEach(visualizations.createBaseVisualization);
|
|
||||||
expressions.registerRenderer(getVislibVisRenderer(core, charts));
|
expressions.registerRenderer(getVislibVisRenderer(core, charts));
|
||||||
|
[createVisTypeVislibVisFn(), createPieVisFn()].forEach(expressions.registerFunction);
|
||||||
|
} else {
|
||||||
|
// Register all vis types
|
||||||
|
visLibVisTypeDefinitions.forEach(visualizations.createBaseVisualization);
|
||||||
|
visualizations.createBaseVisualization(pieVisTypeDefinition);
|
||||||
|
expressions.registerRenderer(getVislibVisRenderer(core, charts));
|
||||||
|
[createVisTypeVislibVisFn(), createPieVisFn()].forEach(expressions.registerFunction);
|
||||||
}
|
}
|
||||||
// Register non-converted types
|
|
||||||
visLibVisTypeDefinitions.forEach(visualizations.createBaseVisualization);
|
|
||||||
visualizations.createBaseVisualization(pieVisTypeDefinition);
|
|
||||||
expressions.registerRenderer(getVislibVisRenderer(core, charts));
|
|
||||||
[createVisTypeVislibVisFn(), createPieVisFn()].forEach(expressions.registerFunction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(core: CoreStart, { data }: VisTypeVislibPluginStartDependencies) {
|
public start(core: CoreStart, { data }: VisTypeVislibPluginStartDependencies) {
|
||||||
|
|
|
@ -21,14 +21,11 @@ import moment from 'moment';
|
||||||
|
|
||||||
import { VisToExpressionAst, getVisSchemas } from '../../visualizations/public';
|
import { VisToExpressionAst, getVisSchemas } from '../../visualizations/public';
|
||||||
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
|
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
|
||||||
|
import type { Dimensions, DateHistogramParams, HistogramParams } from '../../vis_type_xy/public';
|
||||||
|
import { BUCKET_TYPES } from '../../data/public';
|
||||||
|
|
||||||
import { vislibVisName, VisTypeVislibExpressionFunctionDefinition } from './vis_type_vislib_vis_fn';
|
import { vislibVisName, VisTypeVislibExpressionFunctionDefinition } from './vis_type_vislib_vis_fn';
|
||||||
import { BasicVislibParams } from './types';
|
import { BasicVislibParams, VislibChartType } from './types';
|
||||||
import {
|
|
||||||
DateHistogramParams,
|
|
||||||
Dimensions,
|
|
||||||
HistogramParams,
|
|
||||||
} from './vislib/helpers/point_series/point_series';
|
|
||||||
import { getEsaggsFn } from './to_ast_esaggs';
|
import { getEsaggsFn } from './to_ast_esaggs';
|
||||||
|
|
||||||
export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis, params) => {
|
export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis, params) => {
|
||||||
|
@ -47,7 +44,7 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
||||||
|
|
||||||
if (dimensions.x) {
|
if (dimensions.x) {
|
||||||
const xAgg = responseAggs[dimensions.x.accessor] as any;
|
const xAgg = responseAggs[dimensions.x.accessor] as any;
|
||||||
if (xAgg.type.name === 'date_histogram') {
|
if (xAgg.type.name === BUCKET_TYPES.DATE_HISTOGRAM) {
|
||||||
(dimensions.x.params as DateHistogramParams).date = true;
|
(dimensions.x.params as DateHistogramParams).date = true;
|
||||||
const { esUnit, esValue } = xAgg.buckets.getInterval();
|
const { esUnit, esValue } = xAgg.buckets.getInterval();
|
||||||
(dimensions.x.params as DateHistogramParams).intervalESUnit = esUnit;
|
(dimensions.x.params as DateHistogramParams).intervalESUnit = esUnit;
|
||||||
|
@ -57,7 +54,7 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
||||||
.asMilliseconds();
|
.asMilliseconds();
|
||||||
(dimensions.x.params as DateHistogramParams).format = xAgg.buckets.getScaledDateFormat();
|
(dimensions.x.params as DateHistogramParams).format = xAgg.buckets.getScaledDateFormat();
|
||||||
(dimensions.x.params as DateHistogramParams).bounds = xAgg.buckets.getBounds();
|
(dimensions.x.params as DateHistogramParams).bounds = xAgg.buckets.getBounds();
|
||||||
} else if (xAgg.type.name === 'histogram') {
|
} else if (xAgg.type.name === BUCKET_TYPES.HISTOGRAM) {
|
||||||
const intervalParam = xAgg.type.paramByName('interval');
|
const intervalParam = xAgg.type.paramByName('interval');
|
||||||
const output = { params: {} as any };
|
const output = { params: {} as any };
|
||||||
await intervalParam.modifyAggConfigOnSearchRequestStart(xAgg, vis.data.searchSource, {
|
await intervalParam.modifyAggConfigOnSearchRequestStart(xAgg, vis.data.searchSource, {
|
||||||
|
@ -88,15 +85,15 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
||||||
|
|
||||||
visConfig.dimensions = dimensions;
|
visConfig.dimensions = dimensions;
|
||||||
|
|
||||||
const visTypeXy = buildExpressionFunction<VisTypeVislibExpressionFunctionDefinition>(
|
const visTypeVislib = buildExpressionFunction<VisTypeVislibExpressionFunctionDefinition>(
|
||||||
vislibVisName,
|
vislibVisName,
|
||||||
{
|
{
|
||||||
type: vis.type.name,
|
type: vis.type.name as Exclude<VislibChartType, 'pie'>,
|
||||||
visConfig: JSON.stringify(visConfig),
|
visConfig: JSON.stringify(visConfig),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ast = buildExpression([getEsaggsFn(vis), visTypeXy]);
|
const ast = buildExpression([getEsaggsFn(vis), visTypeVislib]);
|
||||||
|
|
||||||
return ast.toAst();
|
return ast.toAst();
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,6 +29,8 @@ import { BasicVislibParams } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get esaggs expressions function
|
* Get esaggs expressions function
|
||||||
|
* TODO: replace this with vis.data.aggs!.toExpressionAst();
|
||||||
|
* https://github.com/elastic/kibana/issues/61768
|
||||||
* @param vis
|
* @param vis
|
||||||
*/
|
*/
|
||||||
export function getEsaggsFn(vis: Vis<PieVisParams> | Vis<BasicVislibParams>) {
|
export function getEsaggsFn(vis: Vis<PieVisParams> | Vis<BasicVislibParams>) {
|
||||||
|
|
|
@ -17,87 +17,71 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TimeMarker } from './vislib/visualizations/time_marker';
|
import { $Values } from '@kbn/utility-types';
|
||||||
|
import { Position } from '@elastic/charts';
|
||||||
|
|
||||||
|
import { Labels } from '../../charts/public';
|
||||||
import {
|
import {
|
||||||
Positions,
|
CategoryAxis,
|
||||||
ChartModes,
|
Dimensions,
|
||||||
ChartTypes,
|
Grid,
|
||||||
AxisModes,
|
SeriesParam,
|
||||||
AxisTypes,
|
ThresholdLine,
|
||||||
InterpolationModes,
|
ValueAxis,
|
||||||
ScaleTypes,
|
} from '../../vis_type_xy/public';
|
||||||
ThresholdLineStyles,
|
import { TimeMarker } from './vislib/visualizations/time_marker';
|
||||||
} from './utils/collections';
|
|
||||||
import { Labels, Style } from '../../charts/public';
|
/**
|
||||||
import { Dimensions } from './vislib/helpers/point_series/point_series';
|
* Gauge title alignment
|
||||||
|
*/
|
||||||
|
export const Alignment = Object.freeze({
|
||||||
|
Automatic: 'automatic' as const,
|
||||||
|
Horizontal: 'horizontal' as const,
|
||||||
|
Vertical: 'vertical' as const,
|
||||||
|
});
|
||||||
|
export type Alignment = $Values<typeof Alignment>;
|
||||||
|
|
||||||
|
export const GaugeType = Object.freeze({
|
||||||
|
Arc: 'Arc' as const,
|
||||||
|
Circle: 'Circle' as const,
|
||||||
|
});
|
||||||
|
export type GaugeType = $Values<typeof GaugeType>;
|
||||||
|
|
||||||
|
export const VislibChartType = Object.freeze({
|
||||||
|
Histogram: 'histogram' as const,
|
||||||
|
HorizontalBar: 'horizontal_bar' as const,
|
||||||
|
Line: 'line' as const,
|
||||||
|
Pie: 'pie' as const,
|
||||||
|
Area: 'area' as const,
|
||||||
|
PointSeries: 'point_series' as const,
|
||||||
|
Heatmap: 'heatmap' as const,
|
||||||
|
Gauge: 'gauge' as const,
|
||||||
|
Goal: 'goal' as const,
|
||||||
|
Metric: 'metric' as const,
|
||||||
|
});
|
||||||
|
export type VislibChartType = $Values<typeof VislibChartType>;
|
||||||
|
|
||||||
export interface CommonVislibParams {
|
export interface CommonVislibParams {
|
||||||
addTooltip: boolean;
|
addTooltip: boolean;
|
||||||
addLegend: boolean;
|
addLegend: boolean;
|
||||||
legendPosition: Positions;
|
legendPosition: Position;
|
||||||
dimensions: Dimensions;
|
dimensions: Dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Scale {
|
|
||||||
boundsMargin?: number | '';
|
|
||||||
defaultYExtents?: boolean;
|
|
||||||
max?: number | null;
|
|
||||||
min?: number | null;
|
|
||||||
mode?: AxisModes;
|
|
||||||
setYExtents?: boolean;
|
|
||||||
type: ScaleTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ThresholdLine {
|
|
||||||
show: boolean;
|
|
||||||
value: number | null;
|
|
||||||
width: number | null;
|
|
||||||
style: ThresholdLineStyles;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Axis {
|
|
||||||
id: string;
|
|
||||||
labels: Labels;
|
|
||||||
position: Positions;
|
|
||||||
scale: Scale;
|
|
||||||
show: boolean;
|
|
||||||
style: Style;
|
|
||||||
title: { text: string };
|
|
||||||
type: AxisTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValueAxis extends Axis {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SeriesParam {
|
|
||||||
data: { label: string; id: string };
|
|
||||||
drawLinesBetweenPoints: boolean;
|
|
||||||
interpolate: InterpolationModes;
|
|
||||||
lineWidth?: number;
|
|
||||||
mode: ChartModes;
|
|
||||||
show: boolean;
|
|
||||||
showCircles: boolean;
|
|
||||||
type: ChartTypes;
|
|
||||||
valueAxis: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BasicVislibParams extends CommonVislibParams {
|
export interface BasicVislibParams extends CommonVislibParams {
|
||||||
|
type: VislibChartType;
|
||||||
|
addLegend: boolean;
|
||||||
addTimeMarker: boolean;
|
addTimeMarker: boolean;
|
||||||
categoryAxes: Axis[];
|
categoryAxes: CategoryAxis[];
|
||||||
orderBucketsBySum?: boolean;
|
orderBucketsBySum?: boolean;
|
||||||
labels: Labels;
|
labels: Labels;
|
||||||
thresholdLine: ThresholdLine;
|
thresholdLine: ThresholdLine;
|
||||||
valueAxes: ValueAxis[];
|
valueAxes: ValueAxis[];
|
||||||
|
grid: Grid;
|
||||||
gauge?: {
|
gauge?: {
|
||||||
percentageMode: boolean;
|
percentageMode: boolean;
|
||||||
};
|
};
|
||||||
grid: {
|
|
||||||
categoryLines: boolean;
|
|
||||||
valueAxis?: string;
|
|
||||||
};
|
|
||||||
seriesParams: SeriesParam[];
|
seriesParams: SeriesParam[];
|
||||||
times: TimeMarker[];
|
times: TimeMarker[];
|
||||||
type: string;
|
radiusRatio: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,338 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import { $Values } from '@kbn/utility-types';
|
|
||||||
|
|
||||||
import { colorSchemas, Rotates } from '../../../charts/public';
|
|
||||||
|
|
||||||
export const Positions = Object.freeze({
|
|
||||||
RIGHT: 'right' as 'right',
|
|
||||||
LEFT: 'left' as 'left',
|
|
||||||
TOP: 'top' as 'top',
|
|
||||||
BOTTOM: 'bottom' as 'bottom',
|
|
||||||
});
|
|
||||||
export type Positions = $Values<typeof Positions>;
|
|
||||||
|
|
||||||
const getPositions = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.legendPositions.topText', {
|
|
||||||
defaultMessage: 'Top',
|
|
||||||
}),
|
|
||||||
value: Positions.TOP,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.legendPositions.leftText', {
|
|
||||||
defaultMessage: 'Left',
|
|
||||||
}),
|
|
||||||
value: Positions.LEFT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.legendPositions.rightText', {
|
|
||||||
defaultMessage: 'Right',
|
|
||||||
}),
|
|
||||||
value: Positions.RIGHT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.legendPositions.bottomText', {
|
|
||||||
defaultMessage: 'Bottom',
|
|
||||||
}),
|
|
||||||
value: Positions.BOTTOM,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ChartTypes = Object.freeze({
|
|
||||||
LINE: 'line' as 'line',
|
|
||||||
AREA: 'area' as 'area',
|
|
||||||
HISTOGRAM: 'histogram' as 'histogram',
|
|
||||||
});
|
|
||||||
export type ChartTypes = $Values<typeof ChartTypes>;
|
|
||||||
|
|
||||||
const getChartTypes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.chartTypes.lineText', {
|
|
||||||
defaultMessage: 'Line',
|
|
||||||
}),
|
|
||||||
value: ChartTypes.LINE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.chartTypes.areaText', {
|
|
||||||
defaultMessage: 'Area',
|
|
||||||
}),
|
|
||||||
value: ChartTypes.AREA,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.chartTypes.barText', {
|
|
||||||
defaultMessage: 'Bar',
|
|
||||||
}),
|
|
||||||
value: ChartTypes.HISTOGRAM,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ChartModes = Object.freeze({
|
|
||||||
NORMAL: 'normal' as 'normal',
|
|
||||||
STACKED: 'stacked' as 'stacked',
|
|
||||||
});
|
|
||||||
export type ChartModes = $Values<typeof ChartModes>;
|
|
||||||
|
|
||||||
const getChartModes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.chartModes.normalText', {
|
|
||||||
defaultMessage: 'Normal',
|
|
||||||
}),
|
|
||||||
value: ChartModes.NORMAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.chartModes.stackedText', {
|
|
||||||
defaultMessage: 'Stacked',
|
|
||||||
}),
|
|
||||||
value: ChartModes.STACKED,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const InterpolationModes = Object.freeze({
|
|
||||||
LINEAR: 'linear' as 'linear',
|
|
||||||
CARDINAL: 'cardinal' as 'cardinal',
|
|
||||||
STEP_AFTER: 'step-after' as 'step-after',
|
|
||||||
});
|
|
||||||
export type InterpolationModes = $Values<typeof InterpolationModes>;
|
|
||||||
|
|
||||||
const getInterpolationModes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.interpolationModes.straightText', {
|
|
||||||
defaultMessage: 'Straight',
|
|
||||||
}),
|
|
||||||
value: InterpolationModes.LINEAR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.interpolationModes.smoothedText', {
|
|
||||||
defaultMessage: 'Smoothed',
|
|
||||||
}),
|
|
||||||
value: InterpolationModes.CARDINAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.interpolationModes.steppedText', {
|
|
||||||
defaultMessage: 'Stepped',
|
|
||||||
}),
|
|
||||||
value: InterpolationModes.STEP_AFTER,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const AxisTypes = Object.freeze({
|
|
||||||
CATEGORY: 'category' as 'category',
|
|
||||||
VALUE: 'value' as 'value',
|
|
||||||
});
|
|
||||||
export type AxisTypes = $Values<typeof AxisTypes>;
|
|
||||||
|
|
||||||
export const ScaleTypes = Object.freeze({
|
|
||||||
LINEAR: 'linear' as 'linear',
|
|
||||||
LOG: 'log' as 'log',
|
|
||||||
SQUARE_ROOT: 'square root' as 'square root',
|
|
||||||
});
|
|
||||||
export type ScaleTypes = $Values<typeof ScaleTypes>;
|
|
||||||
|
|
||||||
const getScaleTypes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.scaleTypes.linearText', {
|
|
||||||
defaultMessage: 'Linear',
|
|
||||||
}),
|
|
||||||
value: ScaleTypes.LINEAR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.scaleTypes.logText', {
|
|
||||||
defaultMessage: 'Log',
|
|
||||||
}),
|
|
||||||
value: ScaleTypes.LOG,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.scaleTypes.squareRootText', {
|
|
||||||
defaultMessage: 'Square root',
|
|
||||||
}),
|
|
||||||
value: ScaleTypes.SQUARE_ROOT,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const AxisModes = Object.freeze({
|
|
||||||
NORMAL: 'normal' as 'normal',
|
|
||||||
PERCENTAGE: 'percentage' as 'percentage',
|
|
||||||
WIGGLE: 'wiggle' as 'wiggle',
|
|
||||||
SILHOUETTE: 'silhouette' as 'silhouette',
|
|
||||||
});
|
|
||||||
export type AxisModes = $Values<typeof AxisModes>;
|
|
||||||
|
|
||||||
const getAxisModes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.axisModes.normalText', {
|
|
||||||
defaultMessage: 'Normal',
|
|
||||||
}),
|
|
||||||
value: AxisModes.NORMAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.axisModes.percentageText', {
|
|
||||||
defaultMessage: 'Percentage',
|
|
||||||
}),
|
|
||||||
value: AxisModes.PERCENTAGE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.axisModes.wiggleText', {
|
|
||||||
defaultMessage: 'Wiggle',
|
|
||||||
}),
|
|
||||||
value: AxisModes.WIGGLE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.axisModes.silhouetteText', {
|
|
||||||
defaultMessage: 'Silhouette',
|
|
||||||
}),
|
|
||||||
value: AxisModes.SILHOUETTE,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ThresholdLineStyles = Object.freeze({
|
|
||||||
FULL: 'full' as 'full',
|
|
||||||
DASHED: 'dashed' as 'dashed',
|
|
||||||
DOT_DASHED: 'dot-dashed' as 'dot-dashed',
|
|
||||||
});
|
|
||||||
export type ThresholdLineStyles = $Values<typeof ThresholdLineStyles>;
|
|
||||||
|
|
||||||
const getThresholdLineStyles = () => [
|
|
||||||
{
|
|
||||||
value: ThresholdLineStyles.FULL,
|
|
||||||
text: i18n.translate('visTypeVislib.thresholdLine.style.fullText', {
|
|
||||||
defaultMessage: 'Full',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: ThresholdLineStyles.DASHED,
|
|
||||||
text: i18n.translate('visTypeVislib.thresholdLine.style.dashedText', {
|
|
||||||
defaultMessage: 'Dashed',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: ThresholdLineStyles.DOT_DASHED,
|
|
||||||
text: i18n.translate('visTypeVislib.thresholdLine.style.dotdashedText', {
|
|
||||||
defaultMessage: 'Dot-dashed',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const getRotateOptions = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.categoryAxis.rotate.horizontalText', {
|
|
||||||
defaultMessage: 'Horizontal',
|
|
||||||
}),
|
|
||||||
value: Rotates.HORIZONTAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.categoryAxis.rotate.verticalText', {
|
|
||||||
defaultMessage: 'Vertical',
|
|
||||||
}),
|
|
||||||
value: Rotates.VERTICAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.categoryAxis.rotate.angledText', {
|
|
||||||
defaultMessage: 'Angled',
|
|
||||||
}),
|
|
||||||
value: Rotates.ANGLED,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const GaugeTypes = Object.freeze({
|
|
||||||
ARC: 'Arc' as 'Arc',
|
|
||||||
CIRCLE: 'Circle' as 'Circle',
|
|
||||||
});
|
|
||||||
export type GaugeTypes = $Values<typeof GaugeTypes>;
|
|
||||||
|
|
||||||
const getGaugeTypes = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.gauge.gaugeTypes.arcText', {
|
|
||||||
defaultMessage: 'Arc',
|
|
||||||
}),
|
|
||||||
value: GaugeTypes.ARC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.gauge.gaugeTypes.circleText', {
|
|
||||||
defaultMessage: 'Circle',
|
|
||||||
}),
|
|
||||||
value: GaugeTypes.CIRCLE,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Alignments = Object.freeze({
|
|
||||||
AUTOMATIC: 'automatic' as 'automatic',
|
|
||||||
HORIZONTAL: 'horizontal' as 'horizontal',
|
|
||||||
VERTICAL: 'vertical' as 'vertical',
|
|
||||||
});
|
|
||||||
export type Alignments = $Values<typeof Alignments>;
|
|
||||||
|
|
||||||
const getAlignments = () => [
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.gauge.alignmentAutomaticTitle', {
|
|
||||||
defaultMessage: 'Automatic',
|
|
||||||
}),
|
|
||||||
value: Alignments.AUTOMATIC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.gauge.alignmentHorizontalTitle', {
|
|
||||||
defaultMessage: 'Horizontal',
|
|
||||||
}),
|
|
||||||
value: Alignments.HORIZONTAL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18n.translate('visTypeVislib.gauge.alignmentVerticalTitle', {
|
|
||||||
defaultMessage: 'Vertical',
|
|
||||||
}),
|
|
||||||
value: Alignments.VERTICAL,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const getConfigCollections = () => ({
|
|
||||||
legendPositions: getPositions(),
|
|
||||||
positions: getPositions(),
|
|
||||||
chartTypes: getChartTypes(),
|
|
||||||
axisModes: getAxisModes(),
|
|
||||||
scaleTypes: getScaleTypes(),
|
|
||||||
chartModes: getChartModes(),
|
|
||||||
interpolationModes: getInterpolationModes(),
|
|
||||||
thresholdLineStyles: getThresholdLineStyles(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const getGaugeCollections = () => ({
|
|
||||||
gaugeTypes: getGaugeTypes(),
|
|
||||||
alignments: getAlignments(),
|
|
||||||
colorSchemas,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getHeatmapCollections = () => ({
|
|
||||||
legendPositions: getPositions(),
|
|
||||||
scales: getScaleTypes(),
|
|
||||||
colorSchemas,
|
|
||||||
});
|
|
||||||
|
|
||||||
export {
|
|
||||||
getConfigCollections,
|
|
||||||
getGaugeCollections,
|
|
||||||
getHeatmapCollections,
|
|
||||||
getPositions,
|
|
||||||
getRotateOptions,
|
|
||||||
getScaleTypes,
|
|
||||||
getInterpolationModes,
|
|
||||||
getChartTypes,
|
|
||||||
getChartModes,
|
|
||||||
getAxisModes,
|
|
||||||
};
|
|
|
@ -26,9 +26,13 @@ import { ChartsPluginSetup } from '../../charts/public';
|
||||||
|
|
||||||
import { VisTypeVislibCoreSetup } from './plugin';
|
import { VisTypeVislibCoreSetup } from './plugin';
|
||||||
import { VislibRenderValue, vislibVisName } from './vis_type_vislib_vis_fn';
|
import { VislibRenderValue, vislibVisName } from './vis_type_vislib_vis_fn';
|
||||||
|
import { VislibChartType } from './types';
|
||||||
|
import { PieRenderValue } from './pie_fn';
|
||||||
|
|
||||||
function shouldShowNoResultsMessage(visData: any, visType: string): boolean {
|
const VislibWrapper = lazy(() => import('./vis_wrapper'));
|
||||||
if (['goal', 'gauge'].includes(visType)) {
|
|
||||||
|
function shouldShowNoResultsMessage(visData: any, visType: VislibChartType): boolean {
|
||||||
|
if (['goal', 'gauge'].includes(visType as string)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,13 +42,12 @@ function shouldShowNoResultsMessage(visData: any, visType: string): boolean {
|
||||||
return Boolean(isZeroHits);
|
return Boolean(isZeroHits);
|
||||||
}
|
}
|
||||||
|
|
||||||
const VislibWrapper = lazy(() => import('./vis_wrapper'));
|
|
||||||
|
|
||||||
export const getVislibVisRenderer: (
|
export const getVislibVisRenderer: (
|
||||||
core: VisTypeVislibCoreSetup,
|
core: VisTypeVislibCoreSetup,
|
||||||
charts: ChartsPluginSetup
|
charts: ChartsPluginSetup
|
||||||
) => ExpressionRenderDefinition<VislibRenderValue> = (core, charts) => ({
|
) => ExpressionRenderDefinition<VislibRenderValue | PieRenderValue> = (core, charts) => ({
|
||||||
name: vislibVisName,
|
name: vislibVisName,
|
||||||
|
displayName: 'Vislib visualization',
|
||||||
reuseDomNode: true,
|
reuseDomNode: true,
|
||||||
render: async (domNode, config, handlers) => {
|
render: async (domNode, config, handlers) => {
|
||||||
const showNoResult = shouldShowNoResultsMessage(config.visData, config.visType);
|
const showNoResult = shouldShowNoResultsMessage(config.visData, config.visType);
|
||||||
|
|
|
@ -23,18 +23,18 @@ import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressio
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { vislibSeriesResponseHandler } from './vislib/response_handler';
|
import { vislibSeriesResponseHandler } from './vislib/response_handler';
|
||||||
import { BasicVislibParams } from './types';
|
import { BasicVislibParams, VislibChartType } from './types';
|
||||||
|
|
||||||
export const vislibVisName = 'vislib_vis';
|
export const vislibVisName = 'vislib_vis';
|
||||||
|
|
||||||
interface Arguments {
|
interface Arguments {
|
||||||
type: string;
|
type: Exclude<VislibChartType, 'pie'>;
|
||||||
visConfig: string;
|
visConfig: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VislibRenderValue {
|
export interface VislibRenderValue {
|
||||||
visData: any;
|
visType: Exclude<VislibChartType, 'pie'>;
|
||||||
visType: string;
|
visData: unknown;
|
||||||
visConfig: BasicVislibParams;
|
visConfig: BasicVislibParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export const createVisTypeVislibVisFn = (): VisTypeVislibExpressionFunctionDefin
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fn(context, args, handlers) {
|
fn(context, args, handlers) {
|
||||||
const visType = args.type;
|
const visType = args.type as Exclude<VislibChartType, 'pie'>;
|
||||||
const visConfig = JSON.parse(args.visConfig) as BasicVislibParams;
|
const visConfig = JSON.parse(args.visConfig) as BasicVislibParams;
|
||||||
const visData = vislibSeriesResponseHandler(context, visConfig.dimensions);
|
const visData = vislibSeriesResponseHandler(context, visConfig.dimensions);
|
||||||
|
|
||||||
|
|
|
@ -36,3 +36,9 @@ export const visLibVisTypeDefinitions = [
|
||||||
gaugeVisTypeDefinition,
|
gaugeVisTypeDefinition,
|
||||||
goalVisTypeDefinition,
|
goalVisTypeDefinition,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const convertedTypeDefinitions = [
|
||||||
|
heatmapVisTypeDefinition,
|
||||||
|
gaugeVisTypeDefinition,
|
||||||
|
goalVisTypeDefinition,
|
||||||
|
];
|
||||||
|
|
|
@ -27,10 +27,11 @@ import { ChartsPluginSetup } from '../../charts/public';
|
||||||
import { VislibRenderValue } from './vis_type_vislib_vis_fn';
|
import { VislibRenderValue } from './vis_type_vislib_vis_fn';
|
||||||
import { createVislibVisController, VislibVisController } from './vis_controller';
|
import { createVislibVisController, VislibVisController } from './vis_controller';
|
||||||
import { VisTypeVislibCoreSetup } from './plugin';
|
import { VisTypeVislibCoreSetup } from './plugin';
|
||||||
|
import { PieRenderValue } from './pie_fn';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
type VislibWrapperProps = VislibRenderValue & {
|
type VislibWrapperProps = (VislibRenderValue | PieRenderValue) & {
|
||||||
core: VisTypeVislibCoreSetup;
|
core: VisTypeVislibCoreSetup;
|
||||||
charts: ChartsPluginSetup;
|
charts: ChartsPluginSetup;
|
||||||
handlers: IInterpreterRenderHandlers;
|
handlers: IInterpreterRenderHandlers;
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
// NOTE: Some of the styles attempt to align with the TSVB legend
|
// NOTE: Some of the styles attempt to align with the TSVB legend
|
||||||
|
|
||||||
$visLegendWidth: 150px;
|
$visLegendWidth: 150px;
|
||||||
$visColorPickerWidth: $euiSizeM * 10;
|
|
||||||
$visLegendLineHeight: $euiSize;
|
|
||||||
|
|
||||||
.visLegend__toggle {
|
.visLegend__toggle {
|
||||||
border-radius: $euiBorderRadius;
|
border-radius: $euiBorderRadius;
|
||||||
|
@ -81,20 +79,3 @@ $visLegendLineHeight: $euiSize;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.visLegend__valueColorPicker {
|
|
||||||
width: ($euiSizeL * 8); // 8 columns
|
|
||||||
}
|
|
||||||
|
|
||||||
.visLegend__valueColorPickerDot {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-isSelected {
|
|
||||||
border: $euiSizeXS solid;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -243,7 +243,7 @@ describe('VisLegend Component', () => {
|
||||||
const first = getLegendItems(wrapper).first();
|
const first = getLegendItems(wrapper).first();
|
||||||
first.simulate('click');
|
first.simulate('click');
|
||||||
|
|
||||||
expect(wrapper.exists('.visLegend__valueDetails')).toBe(true);
|
expect(wrapper.exists('.visColorPicker')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -256,8 +256,8 @@ describe('VisLegend Component', () => {
|
||||||
const first = getLegendItems(wrapper).first();
|
const first = getLegendItems(wrapper).first();
|
||||||
first.simulate('click');
|
first.simulate('click');
|
||||||
|
|
||||||
const popover = wrapper.find('.visLegend__valueDetails').first();
|
const popover = wrapper.find('.visColorPicker').first();
|
||||||
const firstColor = popover.find('.visLegend__valueColorPickerDot').first();
|
const firstColor = popover.find('.visColorPicker__valueDot').first();
|
||||||
firstColor.simulate('click');
|
firstColor.simulate('click');
|
||||||
|
|
||||||
const colors = mockState.get('vis.colors');
|
const colors = mockState.get('vis.colors');
|
||||||
|
|
|
@ -78,18 +78,20 @@ export class VisLegend extends PureComponent<VisLegendProps, VisLegendState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
setColor = (label: string, color: string) => (event: BaseSyntheticEvent) => {
|
setColor = (label: string | number, color: string | null, event: BaseSyntheticEvent) => {
|
||||||
if ((event as KeyboardEvent).key && (event as KeyboardEvent).key !== keys.ENTER) {
|
if ((event as KeyboardEvent).key && (event as KeyboardEvent).key !== keys.ENTER) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = this.props.uiState?.get('vis.colors') || {};
|
this.setState({ selectedLabel: null }, () => {
|
||||||
if (colors[label] === color) delete colors[label];
|
const colors = this.props.uiState?.get('vis.colors') || {};
|
||||||
else colors[label] = color;
|
if (colors[label] === color || !color) delete colors[label];
|
||||||
this.props.uiState?.setSilent('vis.colors', null);
|
else colors[label] = color;
|
||||||
this.props.uiState?.set('vis.colors', colors);
|
this.props.uiState?.setSilent('vis.colors', null);
|
||||||
this.props.uiState?.emit('colorChanged');
|
this.props.uiState?.set('vis.colors', colors);
|
||||||
this.refresh();
|
this.props.uiState?.emit('colorChanged');
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
filter = ({ values: data }: LegendItem, negate: boolean) => {
|
filter = ({ values: data }: LegendItem, negate: boolean) => {
|
||||||
|
|
|
@ -18,10 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, useState, BaseSyntheticEvent, KeyboardEvent } from 'react';
|
import React, { memo, useState, BaseSyntheticEvent, KeyboardEvent } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
|
||||||
import {
|
import {
|
||||||
EuiPopover,
|
EuiPopover,
|
||||||
keys,
|
keys,
|
||||||
|
@ -33,7 +31,8 @@ import {
|
||||||
EuiButtonGroupOptionProps,
|
EuiButtonGroupOptionProps,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
import { legendColors, LegendItem } from './models';
|
import { LegendItem } from './models';
|
||||||
|
import { ColorPicker } from '../../../../../charts/public';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: LegendItem;
|
item: LegendItem;
|
||||||
|
@ -45,7 +44,7 @@ interface Props {
|
||||||
onSelect: (label: string | null) => (event?: BaseSyntheticEvent) => void;
|
onSelect: (label: string | null) => (event?: BaseSyntheticEvent) => void;
|
||||||
onHighlight: (event: BaseSyntheticEvent) => void;
|
onHighlight: (event: BaseSyntheticEvent) => void;
|
||||||
onUnhighlight: (event: BaseSyntheticEvent) => void;
|
onUnhighlight: (event: BaseSyntheticEvent) => void;
|
||||||
setColor: (label: string, color: string) => (event: BaseSyntheticEvent) => void;
|
setColor: (label: string, color: string | null, event: BaseSyntheticEvent) => void;
|
||||||
getColor: (label: string) => string;
|
getColor: (label: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,40 +158,14 @@ const VisLegendItemComponent = ({
|
||||||
closePopover={onSelect(null)}
|
closePopover={onSelect(null)}
|
||||||
panelPaddingSize="s"
|
panelPaddingSize="s"
|
||||||
>
|
>
|
||||||
<div className="visLegend__valueDetails">
|
{canFilter && renderFilterBar()}
|
||||||
{canFilter && renderFilterBar()}
|
|
||||||
|
|
||||||
<div className="visLegend__valueColorPicker" role="listbox">
|
<ColorPicker
|
||||||
<span id={`${legendId}ColorPickerDesc`} className="euiScreenReaderOnly">
|
id={legendId}
|
||||||
<FormattedMessage
|
label={item.label}
|
||||||
id="visTypeVislib.vislib.legend.setColorScreenReaderDescription"
|
color={getColor(item.label)}
|
||||||
defaultMessage="Set color for value {legendDataLabel}"
|
onChange={(c, e) => setColor(item.label, c, e)}
|
||||||
values={{ legendDataLabel: item.label }}
|
/>
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
{legendColors.map((color) => (
|
|
||||||
<EuiIcon
|
|
||||||
role="option"
|
|
||||||
tabIndex={0}
|
|
||||||
type="dot"
|
|
||||||
size="l"
|
|
||||||
color={getColor(item.label)}
|
|
||||||
key={color}
|
|
||||||
aria-label={color}
|
|
||||||
aria-describedby={`${legendId}ColorPickerDesc`}
|
|
||||||
aria-selected={color === getColor(item.label)}
|
|
||||||
onClick={setColor(item.label, color)}
|
|
||||||
onKeyPress={setColor(item.label, color)}
|
|
||||||
className={classNames('visLegend__valueColorPickerDot', {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
'visLegend__valueColorPickerDot-isSelected': color === getColor(item.label),
|
|
||||||
})}
|
|
||||||
style={{ color }}
|
|
||||||
data-test-subj={`legendSelectColor-${color}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</EuiPopover>
|
</EuiPopover>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Dimension } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { addToSiri, Serie } from './_add_to_siri';
|
import { addToSiri, Serie } from './_add_to_siri';
|
||||||
import { Point } from './_get_point';
|
import { Point } from './_get_point';
|
||||||
import { Dimension } from './point_series';
|
|
||||||
|
|
||||||
describe('addToSiri', function () {
|
describe('addToSiri', function () {
|
||||||
it('creates a new series the first time it sees an id', function () {
|
it('creates a new series the first time it sees an id', function () {
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getAggId } from '../../../../../vis_type_xy/public';
|
||||||
|
import type { Dimension } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { Point } from './_get_point';
|
import { Point } from './_get_point';
|
||||||
import { Dimension } from './point_series';
|
|
||||||
|
|
||||||
export interface Serie {
|
export interface Serie {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -48,7 +50,7 @@ export function addToSiri(
|
||||||
}
|
}
|
||||||
|
|
||||||
series.set(id, {
|
series.set(id, {
|
||||||
id: id.split('-').pop() as string,
|
id: getAggId(id),
|
||||||
rawId: id,
|
rawId: id,
|
||||||
label: yLabel == null ? id : yLabel,
|
label: yLabel == null ? id : yLabel,
|
||||||
count: 0,
|
count: 0,
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Dimension, Dimensions } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { getAspects } from './_get_aspects';
|
import { getAspects } from './_get_aspects';
|
||||||
import { Dimension, Dimensions, Aspect } from './point_series';
|
import { Aspect } from './point_series';
|
||||||
import { Table, Row } from '../../types';
|
import { Table, Row } from '../../types';
|
||||||
|
|
||||||
describe('getAspects', function () {
|
describe('getAspects', function () {
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Dimensions } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { makeFakeXAspect } from './_fake_x_aspect';
|
import { makeFakeXAspect } from './_fake_x_aspect';
|
||||||
import { Dimensions, Aspects } from './point_series';
|
import { Aspects } from './point_series';
|
||||||
import { Table } from '../../types';
|
import { Table } from '../../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IFieldFormatsRegistry } from '../../../../../data/common';
|
import { IFieldFormatsRegistry } from '../../../../../data/common';
|
||||||
|
|
||||||
import { getPoint } from './_get_point';
|
import { getPoint } from './_get_point';
|
||||||
import { setFormatService } from '../../../services';
|
import { setFormatService } from '../../../services';
|
||||||
import { Aspect } from './point_series';
|
import { Aspect } from './point_series';
|
||||||
|
@ -94,7 +95,12 @@ describe('getPoint', function () {
|
||||||
|
|
||||||
it('should call deserialize', function () {
|
it('should call deserialize', function () {
|
||||||
const seriesAspect = [
|
const seriesAspect = [
|
||||||
{ accessor: '1', format: { id: 'number', params: { pattern: '$' } } } as Aspect,
|
{
|
||||||
|
title: 'series',
|
||||||
|
accessor: '1',
|
||||||
|
format: { id: 'number', params: { pattern: '$' } },
|
||||||
|
params: {},
|
||||||
|
} as Aspect,
|
||||||
];
|
];
|
||||||
getPoint(table, xAspect, seriesAspect, row, 0, yAspect);
|
getPoint(table, xAspect, seriesAspect, row, 0, yAspect);
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import type { DateHistogramParams, HistogramParams } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { initXAxis } from './_init_x_axis';
|
import { initXAxis } from './_init_x_axis';
|
||||||
import { makeFakeXAspect } from './_fake_x_aspect';
|
import { makeFakeXAspect } from './_fake_x_aspect';
|
||||||
import {
|
import { Aspects, Chart, DateHistogramOrdered, HistogramOrdered } from './point_series';
|
||||||
Aspects,
|
|
||||||
Chart,
|
|
||||||
DateHistogramOrdered,
|
|
||||||
DateHistogramParams,
|
|
||||||
HistogramOrdered,
|
|
||||||
HistogramParams,
|
|
||||||
} from './point_series';
|
|
||||||
import { Table, Column } from '../../types';
|
import { Table, Column } from '../../types';
|
||||||
|
|
||||||
describe('initXAxis', function () {
|
describe('initXAxis', function () {
|
||||||
|
@ -110,7 +106,7 @@ describe('initXAxis', function () {
|
||||||
|
|
||||||
it('reads the date interval param from the x agg', function () {
|
it('reads the date interval param from the x agg', function () {
|
||||||
const dateHistogramParams = chart.aspects.x[0].params as DateHistogramParams;
|
const dateHistogramParams = chart.aspects.x[0].params as DateHistogramParams;
|
||||||
dateHistogramParams.interval = 'P1D';
|
dateHistogramParams.interval = moment.duration(1, 'd').asMilliseconds();
|
||||||
dateHistogramParams.intervalESValue = 1;
|
dateHistogramParams.intervalESValue = 1;
|
||||||
dateHistogramParams.intervalESUnit = 'd';
|
dateHistogramParams.intervalESUnit = 'd';
|
||||||
dateHistogramParams.date = true;
|
dateHistogramParams.date = true;
|
||||||
|
|
|
@ -19,8 +19,11 @@
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import type { DateHistogramParams } from '../../../../../vis_type_xy/public/types';
|
||||||
|
|
||||||
import { orderedDateAxis } from './_ordered_date_axis';
|
import { orderedDateAxis } from './_ordered_date_axis';
|
||||||
import { DateHistogramParams, OrderedChart } from './point_series';
|
import { OrderedChart } from './point_series';
|
||||||
|
|
||||||
describe('orderedDateAxis', function () {
|
describe('orderedDateAxis', function () {
|
||||||
const baseArgs = {
|
const baseArgs = {
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { buildPointSeriesData, Dimensions } from './point_series';
|
|
||||||
|
import type { Dimensions } from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
|
import { buildPointSeriesData } from './point_series';
|
||||||
import { Table, Column } from '../../types';
|
import { Table, Column } from '../../types';
|
||||||
import { setFormatService } from '../../../services';
|
import { setFormatService } from '../../../services';
|
||||||
import { Serie } from './_add_to_siri';
|
import { Serie } from './_add_to_siri';
|
||||||
|
|
|
@ -18,6 +18,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Duration } from 'moment';
|
import { Duration } from 'moment';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
Dimension,
|
||||||
|
Dimensions,
|
||||||
|
DateHistogramParams,
|
||||||
|
HistogramParams,
|
||||||
|
} from '../../../../../vis_type_xy/public';
|
||||||
|
|
||||||
import { getSeries } from './_get_series';
|
import { getSeries } from './_get_series';
|
||||||
import { getAspects } from './_get_aspects';
|
import { getAspects } from './_get_aspects';
|
||||||
import { initYAxis } from './_init_y_axis';
|
import { initYAxis } from './_init_y_axis';
|
||||||
|
@ -26,41 +34,6 @@ import { orderedDateAxis } from './_ordered_date_axis';
|
||||||
import { Serie } from './_add_to_siri';
|
import { Serie } from './_add_to_siri';
|
||||||
import { Column, Table } from '../../types';
|
import { Column, Table } from '../../types';
|
||||||
|
|
||||||
export interface DateHistogramParams {
|
|
||||||
date: boolean;
|
|
||||||
interval: number | string;
|
|
||||||
intervalESValue: number;
|
|
||||||
intervalESUnit: string;
|
|
||||||
format: string;
|
|
||||||
bounds?: {
|
|
||||||
min: string | number;
|
|
||||||
max: string | number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface HistogramParams {
|
|
||||||
interval: number;
|
|
||||||
}
|
|
||||||
export interface FakeParams {
|
|
||||||
defaultValue: string;
|
|
||||||
}
|
|
||||||
export interface Dimension {
|
|
||||||
accessor: number;
|
|
||||||
format: {
|
|
||||||
id?: string;
|
|
||||||
params?: { pattern?: string; [key: string]: any };
|
|
||||||
};
|
|
||||||
params: DateHistogramParams | HistogramParams | FakeParams | {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Dimensions {
|
|
||||||
x: Dimension | null;
|
|
||||||
y: Dimension[];
|
|
||||||
z?: Dimension[];
|
|
||||||
series?: Dimension | Dimension[];
|
|
||||||
width?: Dimension[];
|
|
||||||
splitRow?: Dimension[];
|
|
||||||
splitColumn?: Dimension[];
|
|
||||||
}
|
|
||||||
export interface Aspect {
|
export interface Aspect {
|
||||||
accessor: Column['id'];
|
accessor: Column['id'];
|
||||||
column?: Dimension['accessor'];
|
column?: Dimension['accessor'];
|
||||||
|
|
|
@ -21,15 +21,16 @@ import d3 from 'd3';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import MarkdownIt from 'markdown-it';
|
import MarkdownIt from 'markdown-it';
|
||||||
|
|
||||||
|
import { dispatchRenderComplete } from '../../../../kibana_utils/public';
|
||||||
|
|
||||||
|
import { visTypes as chartTypes } from '../visualizations/vis_types';
|
||||||
import { NoResults } from '../errors';
|
import { NoResults } from '../errors';
|
||||||
import { Layout } from './layout/layout';
|
import { Layout } from './layout/layout';
|
||||||
import { ChartTitle } from './chart_title';
|
import { ChartTitle } from './chart_title';
|
||||||
import { Alerts } from './alerts';
|
import { Alerts } from './alerts';
|
||||||
import { Axis } from './axis/axis';
|
import { Axis } from './axis/axis';
|
||||||
import { ChartGrid as Grid } from './chart_grid';
|
import { ChartGrid as Grid } from './chart_grid';
|
||||||
import { visTypes as chartTypes } from '../visualizations/vis_types';
|
|
||||||
import { Binder } from './binder';
|
import { Binder } from './binder';
|
||||||
import { dispatchRenderComplete } from '../../../../kibana_utils/public';
|
|
||||||
|
|
||||||
const markdownIt = new MarkdownIt({
|
const markdownIt = new MarkdownIt({
|
||||||
html: false,
|
html: false,
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"date": true,
|
"date": true,
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"format": "YYYY-MM-DD",
|
"format": "YYYY-MM-DD",
|
||||||
"bounds": {
|
"bounds": {
|
||||||
"min": "2019-05-10T04:00:00.000Z",
|
"min": "2019-05-10T04:00:00.000Z",
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
},
|
},
|
||||||
"xAxisLabel": "timestamp per day",
|
"xAxisLabel": "timestamp per day",
|
||||||
"ordered": {
|
"ordered": {
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"date": true,
|
"date": true,
|
||||||
"min": 1557460800000,
|
"min": 1557460800000,
|
||||||
"max": 1557656337342
|
"max": 1557656337342
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"date": true,
|
"date": true,
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"format": "YYYY-MM-DD",
|
"format": "YYYY-MM-DD",
|
||||||
"bounds": {
|
"bounds": {
|
||||||
"min": "2019-05-10T04:00:00.000Z",
|
"min": "2019-05-10T04:00:00.000Z",
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
},
|
},
|
||||||
"xAxisLabel": "timestamp per day",
|
"xAxisLabel": "timestamp per day",
|
||||||
"ordered": {
|
"ordered": {
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"date": true,
|
"date": true,
|
||||||
"min": 1557460800000,
|
"min": 1557460800000,
|
||||||
"max": 1557656337342
|
"max": 1557656337342
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"date": true,
|
"date": true,
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"format": "YYYY-MM-DD",
|
"format": "YYYY-MM-DD",
|
||||||
"bounds": {
|
"bounds": {
|
||||||
"min": "2019-05-10T04:00:00.000Z",
|
"min": "2019-05-10T04:00:00.000Z",
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"date": true,
|
"date": true,
|
||||||
"interval": "P1D",
|
"interval": 86400000,
|
||||||
"format": "YYYY-MM-DD",
|
"format": "YYYY-MM-DD",
|
||||||
"bounds": {
|
"bounds": {
|
||||||
"min": "2019-05-10T04:00:00.000Z",
|
"min": "2019-05-10T04:00:00.000Z",
|
||||||
|
|
|
@ -22,8 +22,8 @@ import { Chart } from './_chart';
|
||||||
import { gaugeTypes } from './gauges/gauge_types';
|
import { gaugeTypes } from './gauges/gauge_types';
|
||||||
|
|
||||||
export class GaugeChart extends Chart {
|
export class GaugeChart extends Chart {
|
||||||
constructor(handler, chartEl, chartData, deps) {
|
constructor(handler, chartEl, chartData, uiSettings) {
|
||||||
super(handler, chartEl, chartData, deps);
|
super(handler, chartEl, chartData, uiSettings);
|
||||||
this.gaugeConfig = handler.visConfig.get('gauge', {});
|
this.gaugeConfig = handler.visConfig.get('gauge', {});
|
||||||
this.gauge = new gaugeTypes[this.gaugeConfig.type](this);
|
this.gauge = new gaugeTypes[this.gaugeConfig.type](this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ const defaults = {
|
||||||
* @param chartData {Object} Elasticsearch query results for this specific chart
|
* @param chartData {Object} Elasticsearch query results for this specific chart
|
||||||
*/
|
*/
|
||||||
export class HeatmapChart extends PointSeries {
|
export class HeatmapChart extends PointSeries {
|
||||||
constructor(handler, chartEl, chartData, seriesConfigArgs, deps) {
|
constructor(handler, chartEl, chartData, seriesConfigArgs, core) {
|
||||||
super(handler, chartEl, chartData, seriesConfigArgs, deps);
|
super(handler, chartEl, chartData, seriesConfigArgs, core);
|
||||||
|
|
||||||
this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults);
|
this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ import { UiSettingsParams } from 'kibana/server';
|
||||||
import { DIMMING_OPACITY_SETTING, HEATMAP_MAX_BUCKETS_SETTING } from '../common';
|
import { DIMMING_OPACITY_SETTING, HEATMAP_MAX_BUCKETS_SETTING } from '../common';
|
||||||
|
|
||||||
export const uiSettings: Record<string, UiSettingsParams> = {
|
export const uiSettings: Record<string, UiSettingsParams> = {
|
||||||
|
// TODO: move this to vis_type_xy when vislib is removed
|
||||||
|
// https://github.com/elastic/kibana/issues/56143
|
||||||
[DIMMING_OPACITY_SETTING]: {
|
[DIMMING_OPACITY_SETTING]: {
|
||||||
name: i18n.translate('visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle', {
|
name: i18n.translate('visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle', {
|
||||||
defaultMessage: 'Dimming opacity',
|
defaultMessage: 'Dimming opacity',
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
Contains the new xy-axis chart using the elastic-charts library, which will eventually
|
Contains the new xy-axis chart using the elastic-charts library, which will eventually
|
||||||
replace the vislib xy-axis (bar, area, line) charts.
|
replace the vislib xy-axis charts including bar, area, and line.
|
||||||
|
|
37
src/plugins/vis_type_xy/common/index.ts
Normal file
37
src/plugins/vis_type_xy/common/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { $Values } from '@kbn/utility-types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of charts able to render
|
||||||
|
*/
|
||||||
|
export const ChartType = Object.freeze({
|
||||||
|
Line: 'line' as const,
|
||||||
|
Area: 'area' as const,
|
||||||
|
Histogram: 'histogram' as const,
|
||||||
|
});
|
||||||
|
export type ChartType = $Values<typeof ChartType>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of xy visualizations
|
||||||
|
*/
|
||||||
|
export type XyVisType = ChartType | 'horizontal_bar';
|
||||||
|
|
||||||
|
export const CHARTS_LIBRARY = 'visualization:visualize:chartsLibrary';
|
24
src/plugins/vis_type_xy/jest.config.js
Normal file
24
src/plugins/vis_type_xy/jest.config.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preset: '@kbn/test',
|
||||||
|
rootDir: '../../..',
|
||||||
|
roots: ['<rootDir>/src/plugins/vis_type_xy'],
|
||||||
|
};
|
|
@ -3,5 +3,6 @@
|
||||||
"version": "kibana",
|
"version": "kibana",
|
||||||
"server": true,
|
"server": true,
|
||||||
"ui": true,
|
"ui": true,
|
||||||
"requiredPlugins": ["charts", "expressions", "visualizations"]
|
"requiredPlugins": ["charts", "data", "expressions", "visualizations"],
|
||||||
|
"requiredBundles": ["kibanaUtils", "visDefaultEditor"]
|
||||||
}
|
}
|
||||||
|
|
22
src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap
generated
Normal file
22
src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap
generated
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`xy vis toExpressionAst function should match basic snapshot 1`] = `
|
||||||
|
Object {
|
||||||
|
"addArgument": [Function],
|
||||||
|
"arguments": Object {
|
||||||
|
"type": Array [
|
||||||
|
"area",
|
||||||
|
],
|
||||||
|
"visConfig": Array [
|
||||||
|
"{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"getArgument": [Function],
|
||||||
|
"name": "xy_vis",
|
||||||
|
"removeArgument": [Function],
|
||||||
|
"replaceArgument": [Function],
|
||||||
|
"toAst": [Function],
|
||||||
|
"toString": [Function],
|
||||||
|
"type": "expression_function_builder",
|
||||||
|
}
|
||||||
|
`;
|
7
src/plugins/vis_type_xy/public/_chart.scss
Normal file
7
src/plugins/vis_type_xy/public/_chart.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.xyChart__container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
.detailedTooltip {
|
||||||
|
@include euiToolTipStyle('s');
|
||||||
|
pointer-events: none;
|
||||||
|
max-width: $euiSizeXL * 10;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: $euiSizeS;
|
||||||
|
|
||||||
|
table {
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
padding: $euiSizeXS;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailedTooltip__header {
|
||||||
|
> :last-child {
|
||||||
|
margin-bottom: $euiSizeS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailedTooltip__labelContainer,
|
||||||
|
.detailedTooltip__valueContainer {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailedTooltip__label {
|
||||||
|
font-weight: $euiFontWeightMedium;
|
||||||
|
color: shade($euiColorGhost, 20%);
|
||||||
|
}
|
142
src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
Normal file
142
src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CustomTooltip,
|
||||||
|
TooltipValue,
|
||||||
|
TooltipValueFormatter,
|
||||||
|
XYChartSeriesIdentifier,
|
||||||
|
} from '@elastic/charts';
|
||||||
|
|
||||||
|
import { BUCKET_TYPES } from '../../../data/public';
|
||||||
|
|
||||||
|
import { Aspects } from '../types';
|
||||||
|
|
||||||
|
import './_detailed_tooltip.scss';
|
||||||
|
import { fillEmptyValue } from '../utils/get_series_name_fn';
|
||||||
|
import { COMPLEX_SPLIT_ACCESSOR } from '../utils/accessors';
|
||||||
|
|
||||||
|
interface TooltipData {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTooltipData = (
|
||||||
|
aspects: Aspects,
|
||||||
|
header: TooltipValue | null,
|
||||||
|
value: TooltipValue
|
||||||
|
): TooltipData[] => {
|
||||||
|
const data: TooltipData[] = [];
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
const xFormatter =
|
||||||
|
aspects.x.aggType === BUCKET_TYPES.DATE_RANGE || aspects.x.aggType === BUCKET_TYPES.RANGE
|
||||||
|
? null
|
||||||
|
: aspects.x.formatter;
|
||||||
|
data.push({
|
||||||
|
label: aspects.x.title,
|
||||||
|
value: xFormatter ? xFormatter(header.value) : `${header.value}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueSeries = value.seriesIdentifier as XYChartSeriesIdentifier;
|
||||||
|
const yAccessor = aspects.y.find(({ accessor }) => accessor === valueSeries.yAccessor) ?? null;
|
||||||
|
|
||||||
|
if (yAccessor) {
|
||||||
|
data.push({
|
||||||
|
label: yAccessor.title,
|
||||||
|
value: yAccessor.formatter ? yAccessor.formatter(value.value) : `${value.value}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aspects.z && !isNil(value.markValue)) {
|
||||||
|
data.push({
|
||||||
|
label: aspects.z.title,
|
||||||
|
value: aspects.z.formatter ? aspects.z.formatter(value.markValue) : `${value.markValue}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
valueSeries.splitAccessors.forEach((splitValue, key) => {
|
||||||
|
const split = (aspects.series ?? []).find(({ accessor }, i) => {
|
||||||
|
return accessor === key || key === `${COMPLEX_SPLIT_ACCESSOR}::${i}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (split) {
|
||||||
|
data.push({
|
||||||
|
label: split?.title,
|
||||||
|
value:
|
||||||
|
split?.formatter && !key.toString().startsWith(COMPLEX_SPLIT_ACCESSOR)
|
||||||
|
? split?.formatter(splitValue)
|
||||||
|
: `${splitValue}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderData = ({ label, value: rawValue }: TooltipData, index: number) => {
|
||||||
|
const value = fillEmptyValue(rawValue);
|
||||||
|
return label && value ? (
|
||||||
|
<tr key={label + value + index}>
|
||||||
|
<td className="detailedTooltip__label">
|
||||||
|
<div className="detailedTooltip__labelContainer">{label}</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="detailedTooltip__value">
|
||||||
|
<div className="detailedTooltip__valueContainer">{value}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDetailedTooltip = (aspects: Aspects) => (
|
||||||
|
headerFormatter?: TooltipValueFormatter
|
||||||
|
): CustomTooltip => {
|
||||||
|
return function DetailedTooltip({ header, values }) {
|
||||||
|
// Note: first value is not necessarily the closest value
|
||||||
|
// To be fixed with https://github.com/elastic/elastic-charts/issues/835
|
||||||
|
// TODO: Allow multiple values to be displayed in tooltip
|
||||||
|
const highlightedValue = values.find(({ isHighlighted }) => isHighlighted);
|
||||||
|
|
||||||
|
if (!highlightedValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltipData = getTooltipData(aspects, header, highlightedValue);
|
||||||
|
|
||||||
|
if (tooltipData.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="detailedTooltip">
|
||||||
|
{headerFormatter && header && (
|
||||||
|
<div className="detailedTooltip__header">{headerFormatter(header)}</div>
|
||||||
|
)}
|
||||||
|
<table>
|
||||||
|
<tbody>{tooltipData.map(renderData)}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
25
src/plugins/vis_type_xy/public/components/index.ts
Normal file
25
src/plugins/vis_type_xy/public/components/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { XYAxis } from './xy_axis';
|
||||||
|
export { XYEndzones } from './xy_endzones';
|
||||||
|
export { XYCurrentTime } from './xy_current_time';
|
||||||
|
export { XYSettings } from './xy_settings';
|
||||||
|
export { XYThresholdLine } from './xy_threshold_line';
|
||||||
|
export { SplitChartWarning } from './split_chart_warning';
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import { EuiLink, EuiCallOut } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
|
import { getDocLinks } from '../services';
|
||||||
|
|
||||||
|
export const SplitChartWarning: FC = () => {
|
||||||
|
const advancedSettingsLink = getDocLinks().links.management.visualizationSettings;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiCallOut
|
||||||
|
title={i18n.translate('visTypeXy.splitChartWarning.title', {
|
||||||
|
defaultMessage: 'Warning',
|
||||||
|
})}
|
||||||
|
color="warning"
|
||||||
|
iconType="help"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="visTypeXy.splitChartWarning.content"
|
||||||
|
defaultMessage="The new charts library does not support split chart aggregation. Please disable the {link} advanced setting to use split chart aggregation."
|
||||||
|
values={{
|
||||||
|
link: (
|
||||||
|
<EuiLink href={advancedSettingsLink} target="_blank" external>
|
||||||
|
<FormattedMessage
|
||||||
|
id="visTypeXy.splitChartWarning.link"
|
||||||
|
defaultMessage="Charts library"
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EuiCallOut>
|
||||||
|
);
|
||||||
|
};
|
55
src/plugins/vis_type_xy/public/components/xy_axis.tsx
Normal file
55
src/plugins/vis_type_xy/public/components/xy_axis.tsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import { Axis } from '@elastic/charts';
|
||||||
|
|
||||||
|
import { AxisConfig } from '../types';
|
||||||
|
|
||||||
|
type XYAxisPros = AxisConfig<any>;
|
||||||
|
|
||||||
|
export const XYAxis: FC<XYAxisPros> = ({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
show,
|
||||||
|
position,
|
||||||
|
groupId,
|
||||||
|
grid,
|
||||||
|
ticks,
|
||||||
|
domain,
|
||||||
|
style,
|
||||||
|
integersOnly,
|
||||||
|
}) => (
|
||||||
|
<Axis
|
||||||
|
id={`${id}__axis`}
|
||||||
|
groupId={groupId}
|
||||||
|
hide={!show}
|
||||||
|
title={title}
|
||||||
|
style={style}
|
||||||
|
domain={domain}
|
||||||
|
position={position}
|
||||||
|
integersOnly={integersOnly}
|
||||||
|
showGridLines={grid?.show}
|
||||||
|
tickFormat={ticks?.formatter}
|
||||||
|
labelFormat={ticks?.labelFormatter}
|
||||||
|
showOverlappingLabels={ticks?.showOverlappingLabels}
|
||||||
|
showDuplicatedTicks={ticks?.showDuplicates}
|
||||||
|
/>
|
||||||
|
);
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { DomainRange } from '@elastic/charts';
|
||||||
|
import { CurrentTime } from '../../../charts/public';
|
||||||
|
|
||||||
|
interface XYCurrentTime {
|
||||||
|
enabled: boolean;
|
||||||
|
isDarkMode: boolean;
|
||||||
|
domain?: DomainRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const XYCurrentTime: FC<XYCurrentTime> = ({ enabled, isDarkMode, domain }) => {
|
||||||
|
if (!enabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainEnd = domain && 'max' in domain ? domain.max : undefined;
|
||||||
|
return <CurrentTime isDarkMode={isDarkMode} domainEnd={domainEnd} />;
|
||||||
|
};
|
68
src/plugins/vis_type_xy/public/components/xy_endzones.tsx
Normal file
68
src/plugins/vis_type_xy/public/components/xy_endzones.tsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import { DomainRange } from '@elastic/charts';
|
||||||
|
|
||||||
|
import { Endzones } from '../../../charts/public';
|
||||||
|
|
||||||
|
interface XYEndzones {
|
||||||
|
enabled: boolean;
|
||||||
|
isDarkMode: boolean;
|
||||||
|
isFullBin: boolean;
|
||||||
|
hideTooltips?: boolean;
|
||||||
|
domain?: DomainRange;
|
||||||
|
adjustedDomain?: DomainRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const XYEndzones: FC<XYEndzones> = ({
|
||||||
|
enabled,
|
||||||
|
isDarkMode,
|
||||||
|
isFullBin,
|
||||||
|
hideTooltips,
|
||||||
|
domain,
|
||||||
|
adjustedDomain,
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
enabled &&
|
||||||
|
domain &&
|
||||||
|
adjustedDomain &&
|
||||||
|
'min' in domain &&
|
||||||
|
'max' in domain &&
|
||||||
|
domain.minInterval !== undefined &&
|
||||||
|
'min' in adjustedDomain &&
|
||||||
|
'max' in adjustedDomain
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Endzones
|
||||||
|
isFullBin={isFullBin}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
domainStart={domain.min}
|
||||||
|
domainEnd={domain.max}
|
||||||
|
interval={domain.minInterval}
|
||||||
|
domainMin={adjustedDomain.min}
|
||||||
|
domainMax={adjustedDomain.max}
|
||||||
|
hideTooltips={hideTooltips}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
182
src/plugins/vis_type_xy/public/components/xy_settings.tsx
Normal file
182
src/plugins/vis_type_xy/public/components/xy_settings.tsx
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Direction,
|
||||||
|
Settings,
|
||||||
|
DomainRange,
|
||||||
|
Position,
|
||||||
|
PartialTheme,
|
||||||
|
ElementClickListener,
|
||||||
|
BrushEndListener,
|
||||||
|
RenderChangeListener,
|
||||||
|
LegendAction,
|
||||||
|
LegendColorPicker,
|
||||||
|
TooltipProps,
|
||||||
|
TickFormatter,
|
||||||
|
} from '@elastic/charts';
|
||||||
|
|
||||||
|
import { renderEndzoneTooltip } from '../../../charts/public';
|
||||||
|
|
||||||
|
import { getThemeService, getUISettings } from '../services';
|
||||||
|
import { VisConfig } from '../types';
|
||||||
|
import { fillEmptyValue } from '../utils/get_series_name_fn';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
/**
|
||||||
|
* Flag used to enable debugState on elastic charts
|
||||||
|
*/
|
||||||
|
_echDebugStateFlag?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type XYSettingsProps = Pick<
|
||||||
|
VisConfig,
|
||||||
|
| 'markSizeRatio'
|
||||||
|
| 'rotation'
|
||||||
|
| 'enableHistogramMode'
|
||||||
|
| 'tooltip'
|
||||||
|
| 'isTimeChart'
|
||||||
|
| 'xAxis'
|
||||||
|
| 'orderBucketsBySum'
|
||||||
|
> & {
|
||||||
|
xDomain?: DomainRange;
|
||||||
|
adjustedXDomain?: DomainRange;
|
||||||
|
showLegend: boolean;
|
||||||
|
onElementClick: ElementClickListener;
|
||||||
|
onBrushEnd?: BrushEndListener;
|
||||||
|
onRenderChange: RenderChangeListener;
|
||||||
|
legendAction?: LegendAction;
|
||||||
|
legendColorPicker: LegendColorPicker;
|
||||||
|
legendPosition: Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const XYSettings: FC<XYSettingsProps> = ({
|
||||||
|
markSizeRatio,
|
||||||
|
rotation,
|
||||||
|
enableHistogramMode,
|
||||||
|
tooltip,
|
||||||
|
isTimeChart,
|
||||||
|
xAxis,
|
||||||
|
orderBucketsBySum,
|
||||||
|
xDomain,
|
||||||
|
adjustedXDomain,
|
||||||
|
showLegend,
|
||||||
|
onElementClick,
|
||||||
|
onBrushEnd,
|
||||||
|
onRenderChange,
|
||||||
|
legendAction,
|
||||||
|
legendColorPicker,
|
||||||
|
legendPosition,
|
||||||
|
}) => {
|
||||||
|
const themeService = getThemeService();
|
||||||
|
const theme = themeService.useChartsTheme();
|
||||||
|
const baseTheme = themeService.useChartsBaseTheme();
|
||||||
|
const dimmingOpacity = getUISettings().get<number | undefined>('visualization:dimmingOpacity');
|
||||||
|
const fontSize =
|
||||||
|
typeof theme.barSeriesStyle?.displayValue?.fontSize === 'number'
|
||||||
|
? { min: theme.barSeriesStyle?.displayValue?.fontSize }
|
||||||
|
: theme.barSeriesStyle?.displayValue?.fontSize ?? { min: 8 };
|
||||||
|
|
||||||
|
const themeOverrides: PartialTheme = {
|
||||||
|
markSizeRatio,
|
||||||
|
sharedStyle: {
|
||||||
|
unhighlighted: {
|
||||||
|
opacity: dimmingOpacity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
barSeriesStyle: {
|
||||||
|
displayValue: {
|
||||||
|
fontSize,
|
||||||
|
alignment: {
|
||||||
|
horizontal: 'center',
|
||||||
|
vertical: 'middle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axes: {
|
||||||
|
axisTitle: {
|
||||||
|
padding: {
|
||||||
|
outer: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chartMargins:
|
||||||
|
legendPosition === Position.Top || legendPosition === Position.Right
|
||||||
|
? {
|
||||||
|
bottom: (theme.chartMargins?.bottom ?? 0) + 10,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
right: (theme.chartMargins?.right ?? 0) + 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerValueFormatter: TickFormatter<any> | undefined = xAxis.ticks?.formatter
|
||||||
|
? (value) => fillEmptyValue(xAxis.ticks?.formatter?.(value)) ?? ''
|
||||||
|
: undefined;
|
||||||
|
const headerFormatter =
|
||||||
|
isTimeChart && xDomain && adjustedXDomain
|
||||||
|
? renderEndzoneTooltip(
|
||||||
|
xDomain.minInterval,
|
||||||
|
'min' in xDomain ? xDomain.min : undefined,
|
||||||
|
'max' in xDomain ? xDomain.max : undefined,
|
||||||
|
headerValueFormatter,
|
||||||
|
!tooltip.detailedTooltip
|
||||||
|
)
|
||||||
|
: headerValueFormatter &&
|
||||||
|
(tooltip.detailedTooltip ? undefined : ({ value }: any) => headerValueFormatter(value));
|
||||||
|
|
||||||
|
const tooltipProps: TooltipProps = tooltip.detailedTooltip
|
||||||
|
? {
|
||||||
|
...tooltip,
|
||||||
|
customTooltip: tooltip.detailedTooltip(headerFormatter),
|
||||||
|
headerFormatter: undefined,
|
||||||
|
}
|
||||||
|
: { ...tooltip, headerFormatter };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Settings
|
||||||
|
debugState={window._echDebugStateFlag ?? false}
|
||||||
|
xDomain={adjustedXDomain}
|
||||||
|
rotation={rotation}
|
||||||
|
theme={[themeOverrides, theme]}
|
||||||
|
baseTheme={baseTheme}
|
||||||
|
showLegend={showLegend}
|
||||||
|
legendPosition={legendPosition}
|
||||||
|
allowBrushingLastHistogramBucket={isTimeChart}
|
||||||
|
roundHistogramBrushValues={enableHistogramMode && !isTimeChart}
|
||||||
|
legendColorPicker={legendColorPicker}
|
||||||
|
onElementClick={onElementClick}
|
||||||
|
onBrushEnd={onBrushEnd}
|
||||||
|
onRenderChange={onRenderChange}
|
||||||
|
legendAction={legendAction}
|
||||||
|
tooltip={tooltipProps}
|
||||||
|
orderOrdinalBinsBy={
|
||||||
|
orderBucketsBySum
|
||||||
|
? {
|
||||||
|
direction: Direction.Descending,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import { AnnotationDomainTypes, LineAnnotation } from '@elastic/charts';
|
||||||
|
|
||||||
|
import { ThresholdLineConfig } from '../types';
|
||||||
|
|
||||||
|
type XYThresholdLineProps = ThresholdLineConfig & {
|
||||||
|
groupId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const XYThresholdLine: FC<XYThresholdLineProps> = ({
|
||||||
|
show,
|
||||||
|
value: dataValue,
|
||||||
|
color,
|
||||||
|
width,
|
||||||
|
groupId,
|
||||||
|
dash,
|
||||||
|
}) => {
|
||||||
|
if (!show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LineAnnotation
|
||||||
|
id="__threshold_line__"
|
||||||
|
groupId={groupId}
|
||||||
|
domainType={AnnotationDomainTypes.YDomain}
|
||||||
|
dataValues={[{ dataValue }]}
|
||||||
|
style={{
|
||||||
|
line: {
|
||||||
|
stroke: color,
|
||||||
|
strokeWidth: width ?? 2,
|
||||||
|
opacity: 1,
|
||||||
|
dash,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue