This commit is contained in:
parent
1bcb2d4e87
commit
86563373c5
|
@ -268,7 +268,7 @@ heatmap charts.
|
|||
|
||||
|{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
|
||||
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]
|
||||
|
|
|
@ -454,6 +454,9 @@ of buckets to try to represent.
|
|||
==== Visualization
|
||||
|
||||
[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`::
|
||||
**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*.
|
||||
|
|
|
@ -6,7 +6,7 @@ pageLoadAssetSize:
|
|||
beatsManagement: 188135
|
||||
bfetch: 41874
|
||||
canvas: 1065624
|
||||
charts: 159211
|
||||
charts: 195358
|
||||
cloud: 21076
|
||||
console: 46235
|
||||
core: 692684
|
||||
|
@ -98,7 +98,7 @@ pageLoadAssetSize:
|
|||
visTypeTimeseries: 155347
|
||||
visTypeVega: 153861
|
||||
visTypeVislib: 242982
|
||||
visTypeXy: 20255
|
||||
visTypeXy: 113478
|
||||
visualizations: 295169
|
||||
visualize: 57433
|
||||
watcher: 43742
|
||||
|
|
|
@ -62,14 +62,17 @@ describe('Vislib Color Service', () => {
|
|||
|
||||
it('should throw an error if input is not an array', () => {
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(200);
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction('help');
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(true);
|
||||
}).toThrowError();
|
||||
|
||||
|
@ -78,10 +81,12 @@ describe('Vislib Color Service', () => {
|
|||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(nullValue);
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(emptyObject);
|
||||
}).toThrowError();
|
||||
});
|
||||
|
@ -89,14 +94,17 @@ describe('Vislib Color Service', () => {
|
|||
describe('when array is not composed of numbers, strings, or undefined values', () => {
|
||||
it('should throw an error', () => {
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(arrayOfObjects);
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(arrayOfBooleans);
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(arrayOfNullValues);
|
||||
}).toThrowError();
|
||||
});
|
||||
|
@ -113,6 +121,7 @@ describe('Vislib Color Service', () => {
|
|||
}).not.toThrowError();
|
||||
|
||||
expect(() => {
|
||||
// @ts-expect-error
|
||||
colors.createColorLookupFunction(arrayOfUndefinedValues);
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ export class LegacyColorsService {
|
|||
}
|
||||
|
||||
createColorLookupFunction(
|
||||
arrayOfStringsOrNumbers?: any,
|
||||
arrayOfStringsOrNumbers?: Array<string | number>,
|
||||
colorMapping: Partial<Record<string, string>> = {}
|
||||
) {
|
||||
if (!Array.isArray(arrayOfStringsOrNumbers)) {
|
||||
|
@ -67,7 +67,7 @@ export class LegacyColorsService {
|
|||
|
||||
this.mappedColors.mapKeys(arrayOfStringsOrNumbers);
|
||||
|
||||
return (value: string) => {
|
||||
return (value: string | number) => {
|
||||
return colorMapping[value] || this.mappedColors.get(value);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
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 { SelectOption } from './select';
|
||||
|
||||
|
|
|
@ -18,17 +18,22 @@
|
|||
*/
|
||||
|
||||
import { $Values } from '@kbn/utility-types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const ColorModes = Object.freeze({
|
||||
BACKGROUND: 'Background' as 'Background',
|
||||
LABELS: 'Labels' as 'Labels',
|
||||
NONE: 'None' as 'None',
|
||||
export const ColorMode = Object.freeze({
|
||||
Background: 'Background' as 'Background',
|
||||
Labels: 'Labels' as 'Labels',
|
||||
None: 'None' as 'None',
|
||||
});
|
||||
export type ColorModes = $Values<typeof ColorModes>;
|
||||
export type ColorMode = $Values<typeof ColorMode>;
|
||||
|
||||
export const Rotates = Object.freeze({
|
||||
HORIZONTAL: 0,
|
||||
VERTICAL: 90,
|
||||
ANGLED: 75,
|
||||
export const LabelRotation = Object.freeze({
|
||||
Horizontal: 0,
|
||||
Vertical: 90,
|
||||
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 { ColorModes, Rotates } from './collections';
|
||||
export { ColorMode, LabelRotation, defaultCountLabel } from './collections';
|
||||
export { ColorRanges, SetColorRangeValue } from './color_ranges';
|
||||
export { ColorSchemaOptions, SetColorSchemaOptionsValue } from './color_schema';
|
||||
export { ColorSchemaParams, Labels, Style } from './types';
|
||||
|
@ -28,3 +28,7 @@ export { RequiredNumberInputOption } from './required_number_input';
|
|||
export { SelectOption } from './select';
|
||||
export { SwitchOption } from './switch';
|
||||
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 { Rotates } from './collections';
|
||||
import { LabelRotation } from './collections';
|
||||
|
||||
export interface ColorSchemaParams {
|
||||
colorSchema: ColorSchemas;
|
||||
|
@ -29,8 +29,8 @@ export interface Labels {
|
|||
color?: string;
|
||||
filter?: boolean;
|
||||
overwriteColor?: boolean;
|
||||
rotate?: Rotates;
|
||||
show: boolean;
|
||||
rotate?: LabelRotation;
|
||||
show?: boolean;
|
||||
truncate?: number | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,3 +20,4 @@
|
|||
export * from './color_maps';
|
||||
export * from './colors';
|
||||
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.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui';
|
||||
import moment from 'moment-timezone';
|
||||
import { unitOfTime } from 'moment';
|
||||
import moment, { unitOfTime } from 'moment-timezone';
|
||||
import React, { Component } from 'react';
|
||||
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 {
|
||||
AnnotationDomainTypes,
|
||||
Axis,
|
||||
Chart,
|
||||
HistogramBarSeries,
|
||||
LineAnnotation,
|
||||
Position,
|
||||
ScaleType,
|
||||
Settings,
|
||||
RectAnnotation,
|
||||
TooltipValue,
|
||||
TooltipType,
|
||||
ElementClickListener,
|
||||
XYChartElementEvent,
|
||||
|
@ -43,12 +35,17 @@ import {
|
|||
Theme,
|
||||
} from '@elastic/charts';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { EuiChartThemeType } from '@elastic/eui/dist/eui_charts_theme';
|
||||
import { Subscription, combineLatest } from 'rxjs';
|
||||
import { getServices } from '../../../kibana_services';
|
||||
import { Chart as IChart } from '../helpers/point_series';
|
||||
import {
|
||||
CurrentTime,
|
||||
Endzones,
|
||||
getAdjustedInterval,
|
||||
renderEndzoneTooltip,
|
||||
} from '../../../../../charts/public';
|
||||
|
||||
export interface DiscoverHistogramProps {
|
||||
chartData: IChart;
|
||||
|
@ -60,34 +57,6 @@ interface DiscoverHistogramState {
|
|||
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) {
|
||||
if (uiSettings.isDefault('dateFormat:tz')) {
|
||||
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> {
|
||||
public static propTypes = {
|
||||
chartData: PropTypes.object,
|
||||
|
@ -132,10 +80,10 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
|||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.subscription = combineLatest(
|
||||
this.subscription = combineLatest([
|
||||
getServices().theme.chartsTheme$,
|
||||
getServices().theme.chartsBaseTheme$
|
||||
).subscribe(([chartsTheme, chartsBaseTheme]) =>
|
||||
getServices().theme.chartsBaseTheme$,
|
||||
]).subscribe(([chartsTheme, chartsBaseTheme]) =>
|
||||
this.setState({ chartsTheme, chartsBaseTheme })
|
||||
);
|
||||
}
|
||||
|
@ -171,40 +119,6 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
|||
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() {
|
||||
const uiSettings = getServices().uiSettings;
|
||||
const timeZone = getTimezone(uiSettings);
|
||||
|
@ -216,8 +130,9 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
|||
}
|
||||
|
||||
const data = chartData.values;
|
||||
const isDarkMode = uiSettings.get('theme:darkMode');
|
||||
|
||||
/**
|
||||
/*
|
||||
* Deprecation: [interval] on [date_histogram] is deprecated, use [fixed_interval] or [calendar_interval].
|
||||
* 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
|
||||
|
@ -232,61 +147,21 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
|||
const domainStart = domain.min.valueOf();
|
||||
const domainEnd = domain.max.valueOf();
|
||||
|
||||
const domainMin = data[0]?.x > domainStart ? domainStart : data[0]?.x;
|
||||
const domainMax = domainEnd - xInterval > lastXValue ? domainEnd - xInterval : lastXValue;
|
||||
const domainMin = Math.min(data[0]?.x, domainStart);
|
||||
const domainMax = Math.max(domainEnd - xInterval, lastXValue);
|
||||
|
||||
const xDomain = {
|
||||
min: domainMin,
|
||||
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 = {
|
||||
headerFormatter: this.renderBarTooltip(xInterval, domainStart, domainEnd),
|
||||
headerFormatter: renderEndzoneTooltip(xInterval, domainStart, domainEnd, this.formatXValue),
|
||||
type: TooltipType.VerticalCursor,
|
||||
};
|
||||
|
||||
|
@ -313,19 +188,14 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
|
|||
tickFormat={this.formatXValue}
|
||||
ticks={10}
|
||||
/>
|
||||
<LineAnnotation
|
||||
id="line-annotation"
|
||||
domainType={AnnotationDomainTypes.XDomain}
|
||||
dataValues={lineAnnotationData}
|
||||
hideTooltips={true}
|
||||
style={lineAnnotationStyle}
|
||||
/>
|
||||
<RectAnnotation
|
||||
dataValues={rectAnnotations}
|
||||
id="rect-annotation"
|
||||
zIndex={2}
|
||||
style={rectAnnotationStyle}
|
||||
hideTooltips={true}
|
||||
<CurrentTime isDarkMode={isDarkMode} domainEnd={domainEnd} />
|
||||
<Endzones
|
||||
isDarkMode={isDarkMode}
|
||||
domainStart={domainStart}
|
||||
domainEnd={domainEnd}
|
||||
interval={xDomain.minInterval}
|
||||
domainMin={xDomain.min}
|
||||
domainMax={xDomain.max}
|
||||
/>
|
||||
<HistogramBarSeries
|
||||
id="discover-histogram"
|
||||
|
|
|
@ -34,7 +34,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[eCommerce] Sales by Category',
|
||||
}),
|
||||
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: '{}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
|
|
@ -56,7 +56,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Flights] Flight Count and Average Ticket Price',
|
||||
}),
|
||||
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:
|
||||
'{"vis":{"legendOpen":true,"colors":{"Average Ticket Price":"#629E51","Flight Count":"#AEA2E0"}}}',
|
||||
description: '',
|
||||
|
@ -133,7 +133,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Flights] Delay Type',
|
||||
}),
|
||||
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: '{}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
@ -176,7 +176,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Flights] Delay Buckets',
|
||||
}),
|
||||
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}}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
@ -198,7 +198,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Flights] Flight Delays',
|
||||
}),
|
||||
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: '{}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
@ -220,7 +220,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Flights] Flight Cancellations',
|
||||
}),
|
||||
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: '{}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
|
|
@ -33,7 +33,7 @@ export const getSavedObjects = (): SavedObject[] => [
|
|||
defaultMessage: '[Logs] Unique Visitors vs. Average Bytes',
|
||||
}),
|
||||
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"}}}',
|
||||
description: '',
|
||||
version: 1,
|
||||
|
|
|
@ -76,10 +76,17 @@ function DefaultEditorAggAdd({
|
|||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
const isSchemaDisabled = (schema: Schema): boolean => {
|
||||
const isMaxedCount = (schema: Schema): boolean => {
|
||||
const count = group.filter((agg) => agg.schema === schema.name).length;
|
||||
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 (
|
||||
<EuiFlexGroup justifyContent="center" responsive={false}>
|
||||
|
@ -109,16 +116,21 @@ function DefaultEditorAggAdd({
|
|||
)}
|
||||
</EuiPopoverTitle>
|
||||
<EuiContextMenuPanel
|
||||
items={schemas.map((schema) => (
|
||||
<EuiContextMenuItem
|
||||
key={`${schema.name}_${schema.title}`}
|
||||
data-test-subj={`visEditorAdd_${groupName}_${schema.title}`}
|
||||
disabled={isPopoverOpen && isSchemaDisabled(schema)}
|
||||
onClick={() => onSelectSchema(schema)}
|
||||
>
|
||||
{schema.title}
|
||||
</EuiContextMenuItem>
|
||||
))}
|
||||
items={schemas.map((schema) => {
|
||||
const maxedCount = isMaxedCount(schema);
|
||||
|
||||
return (
|
||||
<EuiContextMenuItem
|
||||
key={`${schema.name}_${schema.title}`}
|
||||
data-test-subj={`visEditorAdd_${groupName}_${schema.title}`}
|
||||
disabled={isPopoverOpen && isSchemaDisabled(schema, maxedCount)}
|
||||
onClick={() => onSelectSchema(schema)}
|
||||
toolTipContent={schema.tooltip ?? (maxedCount ? maxTooltipText : undefined)}
|
||||
>
|
||||
{schema.title}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
})}
|
||||
/>
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Vis } from 'src/plugins/visualizations/public';
|
||||
import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab';
|
||||
import { Vis } from '../../../../visualizations/public';
|
||||
|
||||
import { VisOptionsProps } from '../../vis_options_props';
|
||||
import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab';
|
||||
|
||||
export interface OptionTab {
|
||||
editor: React.ComponentType<VisOptionsProps | DefaultEditorDataTabProps>;
|
||||
|
|
|
@ -25,6 +25,7 @@ import { EuiErrorBoundary, EuiLoadingChart } from '@elastic/eui';
|
|||
import { EditorRenderProps, IEditorController } from 'src/plugins/visualize/public';
|
||||
import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public';
|
||||
|
||||
// @ts-ignore
|
||||
const DefaultEditor = lazy(() => import('./default_editor'));
|
||||
|
||||
class DefaultEditorController implements IEditorController {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import _, { defaults } from 'lodash';
|
||||
|
||||
import { Optional } from '@kbn/utility-types';
|
||||
|
@ -42,6 +43,8 @@ export interface Schema {
|
|||
hideCustomLabel?: boolean;
|
||||
mustBeFirst?: boolean;
|
||||
aggSettings?: any;
|
||||
disabled?: boolean;
|
||||
tooltip?: ReactNode;
|
||||
}
|
||||
|
||||
export class Schemas implements ISchemas {
|
||||
|
|
|
@ -42,12 +42,14 @@ describe('markdown vis toExpressionAst function', () => {
|
|||
|
||||
it('without params', () => {
|
||||
vis.params = {};
|
||||
// @ts-expect-error
|
||||
const actual = toExpressionAst(vis);
|
||||
expect(actual).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('with params', () => {
|
||||
vis.params = { markdown: '### my markdown', fontSize: 15, openLinksInNewTab: true };
|
||||
// @ts-expect-error
|
||||
const actual = toExpressionAst(vis);
|
||||
expect(actual).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Vis } from '../../visualizations/public';
|
||||
import { VisToExpressionAst } from '../../visualizations/public';
|
||||
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
|
||||
import { MarkdownVisExpressionFunctionDefinition } from './markdown_fn';
|
||||
|
||||
export const toExpressionAst = (vis: Vis) => {
|
||||
export const toExpressionAst: VisToExpressionAst = (vis) => {
|
||||
const { markdown, fontSize, openLinksInNewTab } = vis.params;
|
||||
|
||||
const markdownVis = buildExpressionFunction<MarkdownVisExpressionFunctionDefinition>(
|
||||
|
|
|
@ -31,7 +31,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
|
||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
||||
import {
|
||||
ColorModes,
|
||||
ColorMode,
|
||||
ColorRanges,
|
||||
ColorSchemaOptions,
|
||||
SwitchOption,
|
||||
|
@ -86,7 +86,7 @@ function MetricVisOptions({
|
|||
);
|
||||
|
||||
const setColorMode: EuiButtonGroupProps['onChange'] = useCallback(
|
||||
(id: string) => setMetricValue('metricColorMode', id as ColorModes),
|
||||
(id: string) => setMetricValue('metricColorMode', id as ColorMode),
|
||||
[setMetricValue]
|
||||
);
|
||||
|
||||
|
@ -158,7 +158,7 @@ function MetricVisOptions({
|
|||
colorSchemas={vis.type.editorConfig.collections.colorSchemas}
|
||||
disabled={
|
||||
stateParams.metric.colorsRange.length === 1 ||
|
||||
stateParams.metric.metricColorMode === ColorModes.NONE
|
||||
stateParams.metric.metricColorMode === ColorMode.None
|
||||
}
|
||||
invertColors={stateParams.metric.invertColors}
|
||||
setValue={setMetricValue as SetColorSchemaOptionsValue}
|
||||
|
|
|
@ -27,14 +27,14 @@ import {
|
|||
Style,
|
||||
} from '../../expressions/public';
|
||||
import { visType, DimensionsVisParam, VisParams } from './types';
|
||||
import { ColorSchemas, vislibColorMaps, ColorModes } from '../../charts/public';
|
||||
import { ColorSchemas, vislibColorMaps, ColorMode } from '../../charts/public';
|
||||
|
||||
export type Input = Datatable;
|
||||
|
||||
interface Arguments {
|
||||
percentageMode: boolean;
|
||||
colorSchema: ColorSchemas;
|
||||
colorMode: ColorModes;
|
||||
colorMode: ColorMode;
|
||||
useRanges: boolean;
|
||||
invertColors: boolean;
|
||||
showLabels: boolean;
|
||||
|
@ -86,7 +86,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({
|
|||
colorMode: {
|
||||
types: ['string'],
|
||||
default: '"None"',
|
||||
options: [ColorModes.NONE, ColorModes.LABELS, ColorModes.BACKGROUND],
|
||||
options: [ColorMode.None, ColorMode.Labels, ColorMode.Background],
|
||||
help: i18n.translate('visTypeMetric.function.colorMode.help', {
|
||||
defaultMessage: 'Which part of metric to color',
|
||||
}),
|
||||
|
@ -197,8 +197,8 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({
|
|||
invertColors: args.invertColors,
|
||||
style: {
|
||||
bgFill: args.bgFill,
|
||||
bgColor: args.colorMode === ColorModes.BACKGROUND,
|
||||
labelColor: args.colorMode === ColorModes.LABELS,
|
||||
bgColor: args.colorMode === ColorMode.Background,
|
||||
labelColor: args.colorMode === ColorMode.Labels,
|
||||
subText: args.subText,
|
||||
fontSize,
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
|
||||
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 { Schemas } from '../../vis_default_editor/public';
|
||||
import { toExpressionAst } from './to_ast';
|
||||
|
@ -42,7 +42,7 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
|
|||
percentageMode: false,
|
||||
useRanges: false,
|
||||
colorSchema: ColorSchemas.GreenToRed,
|
||||
metricColorMode: ColorModes.NONE,
|
||||
metricColorMode: ColorMode.None,
|
||||
colorsRange: [{ from: 0, to: 10000 }],
|
||||
labels: {
|
||||
show: true,
|
||||
|
@ -62,19 +62,19 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
|
|||
collections: {
|
||||
metricColorMode: [
|
||||
{
|
||||
id: ColorModes.NONE,
|
||||
id: ColorMode.None,
|
||||
label: i18n.translate('visTypeMetric.colorModes.noneOptionLabel', {
|
||||
defaultMessage: 'None',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ColorModes.LABELS,
|
||||
id: ColorMode.Labels,
|
||||
label: i18n.translate('visTypeMetric.colorModes.labelsOptionLabel', {
|
||||
defaultMessage: 'Labels',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ColorModes.BACKGROUND,
|
||||
id: ColorMode.Background,
|
||||
label: i18n.translate('visTypeMetric.colorModes.backgroundOptionLabel', {
|
||||
defaultMessage: 'Background',
|
||||
}),
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { Range } from '../../expressions/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';
|
||||
|
||||
|
@ -32,7 +32,7 @@ export interface MetricVisParam {
|
|||
percentageMode: boolean;
|
||||
useRanges: boolean;
|
||||
colorSchema: ColorSchemas;
|
||||
metricColorMode: ColorModes;
|
||||
metricColorMode: ColorMode;
|
||||
colorsRange: Range[];
|
||||
labels: Labels;
|
||||
invertColors: boolean;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// 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`] = `
|
||||
<AreaSeries
|
||||
<Connect(SpecInstance)
|
||||
areaSeriesStyle={
|
||||
Object {
|
||||
"area": Object {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// 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`] = `
|
||||
<BarSeries
|
||||
<Connect(SpecInstance)
|
||||
barSeriesStyle={
|
||||
Object {
|
||||
"rect": Object {
|
||||
|
|
|
@ -4,6 +4,5 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"],
|
||||
"optionalPlugins": ["visTypeXy"],
|
||||
"requiredBundles": ["kibanaUtils", "visDefaultEditor"]
|
||||
"requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy"]
|
||||
}
|
||||
|
|
|
@ -17,172 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { palettes } from '@elastic/eui/lib/services';
|
||||
// @ts-ignore
|
||||
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
||||
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||
|
||||
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 { BasicVislibParams } from './types';
|
||||
|
||||
export const areaVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||
name: 'area',
|
||||
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],
|
||||
...(xyVisTypes.area() as BaseVisTypeOptions<BasicVislibParams>),
|
||||
toExpressionAst,
|
||||
visConfig: {
|
||||
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'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
visualization: undefined,
|
||||
};
|
||||
|
|
|
@ -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 { 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 {
|
||||
BasicOptions,
|
||||
ColorRanges,
|
||||
|
@ -34,8 +35,8 @@ import {
|
|||
SetColorSchemaOptionsValue,
|
||||
SetColorRangeValue,
|
||||
} from '../../../../../charts/public';
|
||||
|
||||
import { HeatmapVisParams } from '../../../heatmap';
|
||||
import { ValueAxis } from '../../../types';
|
||||
import { LabelsPanel } from './labels_panel';
|
||||
|
||||
function HeatmapOptions(props: VisOptionsProps<HeatmapVisParams>) {
|
|
@ -23,10 +23,11 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
||||
import { ValueAxis } from '../../../types';
|
||||
import { HeatmapVisParams } from '../../../heatmap';
|
||||
import { VisOptionsProps } from '../../../../../vis_default_editor/public';
|
||||
import { SwitchOption } from '../../../../../charts/public';
|
||||
import { ValueAxis } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { HeatmapVisParams } from '../../../heatmap';
|
||||
|
||||
const VERTICAL_ROTATION = 270;
|
||||
|
|
@ -19,18 +19,15 @@
|
|||
|
||||
import React, { lazy } from 'react';
|
||||
|
||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
||||
import { ValidationVisOptionsProps } from '../common';
|
||||
import { VisOptionsProps } from '../../../../vis_default_editor/public';
|
||||
|
||||
import { GaugeVisParams } from '../../gauge';
|
||||
import { PieVisParams } from '../../pie';
|
||||
import { BasicVislibParams } from '../../types';
|
||||
import { HeatmapVisParams } from '../../heatmap';
|
||||
|
||||
const GaugeOptionsLazy = lazy(() => import('./gauge'));
|
||||
const PieOptionsLazy = lazy(() => import('./pie'));
|
||||
const PointSeriesOptionsLazy = lazy(() => import('./point_series'));
|
||||
const HeatmapOptionsLazy = lazy(() => import('./heatmap'));
|
||||
const MetricsAxisOptionsLazy = lazy(() => import('./metrics_axes'));
|
||||
|
||||
export const GaugeOptions = (props: VisOptionsProps<GaugeVisParams>) => (
|
||||
<GaugeOptionsLazy {...props} />
|
||||
|
@ -38,14 +35,6 @@ export const GaugeOptions = (props: VisOptionsProps<GaugeVisParams>) => (
|
|||
|
||||
export const PieOptions = (props: VisOptionsProps<PieVisParams>) => <PieOptionsLazy {...props} />;
|
||||
|
||||
export const PointSeriesOptions = (props: ValidationVisOptionsProps<BasicVislibParams>) => (
|
||||
<PointSeriesOptionsLazy {...props} />
|
||||
);
|
||||
|
||||
export const HeatmapOptions = (props: VisOptionsProps<HeatmapVisParams>) => (
|
||||
<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 { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
|
||||
import { TruncateLabelsOption } from '../common';
|
||||
import { VisOptionsProps } from '../../../../vis_default_editor/public';
|
||||
import { BasicOptions, SwitchOption } from '../../../../charts/public';
|
||||
import { TruncateLabelsOption } from '../../../../vis_type_xy/public';
|
||||
|
||||
import { PieVisParams } from '../../pie';
|
||||
|
||||
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 { ColorMode, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public';
|
||||
import { RangeValues, Schemas } from '../../vis_default_editor/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 { 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 {
|
||||
backStyle: 'Full';
|
||||
gaugeStyle: 'Full';
|
||||
orientation: 'vertical';
|
||||
type: 'meter';
|
||||
alignment: Alignments;
|
||||
alignment: Alignment;
|
||||
colorsRange: RangeValues[];
|
||||
extendRange: boolean;
|
||||
gaugeType: GaugeTypes;
|
||||
gaugeType: GaugeType;
|
||||
labels: Labels;
|
||||
percentageMode: boolean;
|
||||
outline?: boolean;
|
||||
|
@ -67,20 +68,20 @@ export const gaugeVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
|||
toExpressionAst,
|
||||
visConfig: {
|
||||
defaults: {
|
||||
type: 'gauge',
|
||||
type: VislibChartType.Gauge,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
isDisplayWarning: false,
|
||||
gauge: {
|
||||
alignment: Alignments.AUTOMATIC,
|
||||
alignment: Alignment.Automatic,
|
||||
extendRange: true,
|
||||
percentageMode: false,
|
||||
gaugeType: GaugeTypes.ARC,
|
||||
gaugeType: GaugeType.Arc,
|
||||
gaugeStyle: 'Full',
|
||||
backStyle: 'Full',
|
||||
orientation: 'vertical',
|
||||
colorSchema: ColorSchemas.GreenToRed,
|
||||
gaugeColorMode: ColorModes.LABELS,
|
||||
gaugeColorMode: ColorMode.Labels,
|
||||
colorsRange: [
|
||||
{ from: 0, to: 50 },
|
||||
{ from: 50, to: 75 },
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
|
||||
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 { Schemas } from '../../vis_default_editor/public';
|
||||
import { toExpressionAst } from './to_ast';
|
||||
import { ColorMode, ColorSchemas } from '../../charts/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> = {
|
||||
name: 'goal',
|
||||
|
@ -46,13 +46,13 @@ export const goalVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
|||
verticalSplit: false,
|
||||
autoExtend: false,
|
||||
percentageMode: true,
|
||||
gaugeType: GaugeTypes.ARC,
|
||||
gaugeType: GaugeType.Arc,
|
||||
gaugeStyle: 'Full',
|
||||
backStyle: 'Full',
|
||||
orientation: 'vertical',
|
||||
useRanges: false,
|
||||
colorSchema: ColorSchemas.GreenToRed,
|
||||
gaugeColorMode: ColorModes.NONE,
|
||||
gaugeColorMode: ColorMode.None,
|
||||
colorsRange: [{ from: 0, to: 10000 }],
|
||||
invertColors: false,
|
||||
labels: {
|
||||
|
|
|
@ -18,15 +18,17 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Position } from '@elastic/charts';
|
||||
|
||||
import { RangeValues, Schemas } from '../../vis_default_editor/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 { 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';
|
||||
|
||||
export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams {
|
||||
|
@ -48,15 +50,15 @@ export const heatmapVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
|||
description: i18n.translate('visTypeVislib.heatmap.heatmapDescription', {
|
||||
defaultMessage: 'Shade data in cells in a matrix.',
|
||||
}),
|
||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
|
||||
toExpressionAst,
|
||||
getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
|
||||
visConfig: {
|
||||
defaults: {
|
||||
type: 'heatmap',
|
||||
type: VislibChartType.Heatmap,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
enableHover: false,
|
||||
legendPosition: Positions.RIGHT,
|
||||
legendPosition: Position.Right,
|
||||
times: [],
|
||||
colorsNumber: 4,
|
||||
colorSchema: ColorSchemas.Greens,
|
||||
|
@ -68,9 +70,9 @@ export const heatmapVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
|||
{
|
||||
show: false,
|
||||
id: 'ValueAxis-1',
|
||||
type: AxisTypes.VALUE,
|
||||
type: AxisType.Value,
|
||||
scale: {
|
||||
type: ScaleTypes.LINEAR,
|
||||
type: ScaleType.Linear,
|
||||
defaultYExtents: false,
|
||||
},
|
||||
labels: {
|
||||
|
|
|
@ -17,174 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { palettes } from '@elastic/eui/lib/services';
|
||||
// @ts-ignore
|
||||
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
||||
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||
|
||||
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 { BasicVislibParams } from './types';
|
||||
|
||||
export const histogramVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||
name: 'histogram',
|
||||
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],
|
||||
...(xyVisTypes.histogram() as BaseVisTypeOptions<BasicVislibParams>),
|
||||
toExpressionAst,
|
||||
visConfig: {
|
||||
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'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
visualization: undefined,
|
||||
};
|
||||
|
|
|
@ -17,171 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
||||
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||
|
||||
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 { BasicVislibParams } from './types';
|
||||
|
||||
export const horizontalBarVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||
name: 'horizontal_bar',
|
||||
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],
|
||||
...(xyVisTypes.horizontalBar() as BaseVisTypeOptions<BasicVislibParams>),
|
||||
toExpressionAst,
|
||||
visConfig: {
|
||||
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'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
visualization: undefined,
|
||||
};
|
||||
|
|
|
@ -17,164 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
||||
import { xyVisTypes } from '../../vis_type_xy/public';
|
||||
import { BaseVisTypeOptions } from '../../visualizations/public';
|
||||
|
||||
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 { BasicVislibParams } from './types';
|
||||
|
||||
export const lineVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
|
||||
name: 'line',
|
||||
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],
|
||||
...(xyVisTypes.line() as BaseVisTypeOptions<BasicVislibParams>),
|
||||
toExpressionAst,
|
||||
visConfig: {
|
||||
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'],
|
||||
},
|
||||
]),
|
||||
},
|
||||
visualization: undefined,
|
||||
};
|
||||
|
|
|
@ -18,13 +18,15 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Position } from '@elastic/charts';
|
||||
|
||||
import { AggGroupNames } from '../../data/public';
|
||||
import { Schemas } from '../../vis_default_editor/public';
|
||||
import { PieOptions } from './components/options';
|
||||
import { getPositions, Positions } from './utils/collections';
|
||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
|
||||
import { getPositions } from '../../vis_type_xy/public';
|
||||
|
||||
import { CommonVislibParams } from './types';
|
||||
import { BaseVisTypeOptions, VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
|
||||
import { PieOptions } from './editor';
|
||||
import { toExpressionAst } from './to_ast_pie';
|
||||
|
||||
export interface PieVisParams extends CommonVislibParams {
|
||||
|
@ -52,7 +54,7 @@ export const pieVisTypeDefinition: BaseVisTypeOptions<PieVisParams> = {
|
|||
type: 'pie',
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
legendPosition: Positions.RIGHT,
|
||||
legendPosition: Position.Right,
|
||||
isDonut: true,
|
||||
labels: {
|
||||
show: false,
|
||||
|
|
|
@ -24,6 +24,7 @@ import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressio
|
|||
// @ts-ignore
|
||||
import { vislibSlicesResponseHandler } from './vislib/response_handler';
|
||||
import { PieVisParams } from './pie';
|
||||
import { VislibChartType } from './types';
|
||||
import { vislibVisName } from './vis_type_vislib_vis_fn';
|
||||
|
||||
export const vislibPieName = 'vislib_pie_vis';
|
||||
|
@ -32,9 +33,9 @@ interface Arguments {
|
|||
visConfig: string;
|
||||
}
|
||||
|
||||
interface RenderValue {
|
||||
export interface PieRenderValue {
|
||||
visType: Extract<VislibChartType, 'pie'>;
|
||||
visData: unknown;
|
||||
visType: string;
|
||||
visConfig: PieVisParams;
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,7 @@ export type VisTypeVislibPieExpressionFunctionDefinition = ExpressionFunctionDef
|
|||
typeof vislibPieName,
|
||||
Datatable,
|
||||
Arguments,
|
||||
Render<RenderValue>
|
||||
Render<PieRenderValue>
|
||||
>;
|
||||
|
||||
export const createPieVisFn = (): VisTypeVislibPieExpressionFunctionDefinition => ({
|
||||
|
@ -73,7 +74,7 @@ export const createPieVisFn = (): VisTypeVislibPieExpressionFunctionDefinition =
|
|||
value: {
|
||||
visData,
|
||||
visConfig,
|
||||
visType: 'pie',
|
||||
visType: VislibChartType.Pie,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -19,25 +19,28 @@
|
|||
|
||||
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 { BaseVisTypeOptions, 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 { VisualizationsSetup } from '../../visualizations/public';
|
||||
import { ChartsPluginSetup } from '../../charts/public';
|
||||
import { DataPublicPluginStart } from '../../data/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 { getVislibVisRenderer } from './vis_renderer';
|
||||
import { BasicVislibParams } from './types';
|
||||
|
||||
/** @internal */
|
||||
export interface VisTypeVislibPluginSetupDependencies {
|
||||
expressions: ReturnType<ExpressionsPublicPlugin['setup']>;
|
||||
visualizations: VisualizationsSetup;
|
||||
charts: ChartsPluginSetup;
|
||||
visTypeXy?: VisTypeXyPluginSetup;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -56,23 +59,21 @@ export class VisTypeVislibPlugin
|
|||
|
||||
public async setup(
|
||||
core: VisTypeVislibCoreSetup,
|
||||
{ expressions, visualizations, charts, visTypeXy }: VisTypeVislibPluginSetupDependencies
|
||||
{ expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies
|
||||
) {
|
||||
// if visTypeXy plugin is disabled it's config will be undefined
|
||||
if (!visTypeXy) {
|
||||
const convertedTypes: Array<BaseVisTypeOptions<BasicVislibParams>> = [];
|
||||
const convertedFns: any[] = [];
|
||||
|
||||
// Register legacy vislib types that have been converted
|
||||
convertedFns.forEach(expressions.registerFunction);
|
||||
convertedTypes.forEach(visualizations.createBaseVisualization);
|
||||
if (core.uiSettings.get(CHARTS_LIBRARY)) {
|
||||
// Register only non-replaced vis types
|
||||
convertedTypeDefinitions.forEach(visualizations.createBaseVisualization);
|
||||
visualizations.createBaseVisualization(pieVisTypeDefinition);
|
||||
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) {
|
||||
|
|
|
@ -21,14 +21,11 @@ import moment from 'moment';
|
|||
|
||||
import { VisToExpressionAst, getVisSchemas } from '../../visualizations/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 { BasicVislibParams } from './types';
|
||||
import {
|
||||
DateHistogramParams,
|
||||
Dimensions,
|
||||
HistogramParams,
|
||||
} from './vislib/helpers/point_series/point_series';
|
||||
import { BasicVislibParams, VislibChartType } from './types';
|
||||
import { getEsaggsFn } from './to_ast_esaggs';
|
||||
|
||||
export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis, params) => {
|
||||
|
@ -47,7 +44,7 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
|||
|
||||
if (dimensions.x) {
|
||||
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;
|
||||
const { esUnit, esValue } = xAgg.buckets.getInterval();
|
||||
(dimensions.x.params as DateHistogramParams).intervalESUnit = esUnit;
|
||||
|
@ -57,7 +54,7 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
|||
.asMilliseconds();
|
||||
(dimensions.x.params as DateHistogramParams).format = xAgg.buckets.getScaledDateFormat();
|
||||
(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 output = { params: {} as any };
|
||||
await intervalParam.modifyAggConfigOnSearchRequestStart(xAgg, vis.data.searchSource, {
|
||||
|
@ -88,15 +85,15 @@ export const toExpressionAst: VisToExpressionAst<BasicVislibParams> = async (vis
|
|||
|
||||
visConfig.dimensions = dimensions;
|
||||
|
||||
const visTypeXy = buildExpressionFunction<VisTypeVislibExpressionFunctionDefinition>(
|
||||
const visTypeVislib = buildExpressionFunction<VisTypeVislibExpressionFunctionDefinition>(
|
||||
vislibVisName,
|
||||
{
|
||||
type: vis.type.name,
|
||||
type: vis.type.name as Exclude<VislibChartType, 'pie'>,
|
||||
visConfig: JSON.stringify(visConfig),
|
||||
}
|
||||
);
|
||||
|
||||
const ast = buildExpression([getEsaggsFn(vis), visTypeXy]);
|
||||
const ast = buildExpression([getEsaggsFn(vis), visTypeVislib]);
|
||||
|
||||
return ast.toAst();
|
||||
};
|
||||
|
|
|
@ -29,6 +29,8 @@ import { BasicVislibParams } from './types';
|
|||
|
||||
/**
|
||||
* Get esaggs expressions function
|
||||
* TODO: replace this with vis.data.aggs!.toExpressionAst();
|
||||
* https://github.com/elastic/kibana/issues/61768
|
||||
* @param vis
|
||||
*/
|
||||
export function getEsaggsFn(vis: Vis<PieVisParams> | Vis<BasicVislibParams>) {
|
||||
|
|
|
@ -17,87 +17,71 @@
|
|||
* 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 {
|
||||
Positions,
|
||||
ChartModes,
|
||||
ChartTypes,
|
||||
AxisModes,
|
||||
AxisTypes,
|
||||
InterpolationModes,
|
||||
ScaleTypes,
|
||||
ThresholdLineStyles,
|
||||
} from './utils/collections';
|
||||
import { Labels, Style } from '../../charts/public';
|
||||
import { Dimensions } from './vislib/helpers/point_series/point_series';
|
||||
CategoryAxis,
|
||||
Dimensions,
|
||||
Grid,
|
||||
SeriesParam,
|
||||
ThresholdLine,
|
||||
ValueAxis,
|
||||
} from '../../vis_type_xy/public';
|
||||
import { TimeMarker } from './vislib/visualizations/time_marker';
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
addTooltip: boolean;
|
||||
addLegend: boolean;
|
||||
legendPosition: Positions;
|
||||
legendPosition: Position;
|
||||
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 {
|
||||
type: VislibChartType;
|
||||
addLegend: boolean;
|
||||
addTimeMarker: boolean;
|
||||
categoryAxes: Axis[];
|
||||
categoryAxes: CategoryAxis[];
|
||||
orderBucketsBySum?: boolean;
|
||||
labels: Labels;
|
||||
thresholdLine: ThresholdLine;
|
||||
valueAxes: ValueAxis[];
|
||||
grid: Grid;
|
||||
gauge?: {
|
||||
percentageMode: boolean;
|
||||
};
|
||||
grid: {
|
||||
categoryLines: boolean;
|
||||
valueAxis?: string;
|
||||
};
|
||||
seriesParams: SeriesParam[];
|
||||
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 { VislibRenderValue, vislibVisName } from './vis_type_vislib_vis_fn';
|
||||
import { VislibChartType } from './types';
|
||||
import { PieRenderValue } from './pie_fn';
|
||||
|
||||
function shouldShowNoResultsMessage(visData: any, visType: string): boolean {
|
||||
if (['goal', 'gauge'].includes(visType)) {
|
||||
const VislibWrapper = lazy(() => import('./vis_wrapper'));
|
||||
|
||||
function shouldShowNoResultsMessage(visData: any, visType: VislibChartType): boolean {
|
||||
if (['goal', 'gauge'].includes(visType as string)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -38,13 +42,12 @@ function shouldShowNoResultsMessage(visData: any, visType: string): boolean {
|
|||
return Boolean(isZeroHits);
|
||||
}
|
||||
|
||||
const VislibWrapper = lazy(() => import('./vis_wrapper'));
|
||||
|
||||
export const getVislibVisRenderer: (
|
||||
core: VisTypeVislibCoreSetup,
|
||||
charts: ChartsPluginSetup
|
||||
) => ExpressionRenderDefinition<VislibRenderValue> = (core, charts) => ({
|
||||
) => ExpressionRenderDefinition<VislibRenderValue | PieRenderValue> = (core, charts) => ({
|
||||
name: vislibVisName,
|
||||
displayName: 'Vislib visualization',
|
||||
reuseDomNode: true,
|
||||
render: async (domNode, config, handlers) => {
|
||||
const showNoResult = shouldShowNoResultsMessage(config.visData, config.visType);
|
||||
|
|
|
@ -23,18 +23,18 @@ import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressio
|
|||
|
||||
// @ts-ignore
|
||||
import { vislibSeriesResponseHandler } from './vislib/response_handler';
|
||||
import { BasicVislibParams } from './types';
|
||||
import { BasicVislibParams, VislibChartType } from './types';
|
||||
|
||||
export const vislibVisName = 'vislib_vis';
|
||||
|
||||
interface Arguments {
|
||||
type: string;
|
||||
type: Exclude<VislibChartType, 'pie'>;
|
||||
visConfig: string;
|
||||
}
|
||||
|
||||
export interface VislibRenderValue {
|
||||
visData: any;
|
||||
visType: string;
|
||||
visType: Exclude<VislibChartType, 'pie'>;
|
||||
visData: unknown;
|
||||
visConfig: BasicVislibParams;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ export const createVisTypeVislibVisFn = (): VisTypeVislibExpressionFunctionDefin
|
|||
},
|
||||
},
|
||||
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 visData = vislibSeriesResponseHandler(context, visConfig.dimensions);
|
||||
|
||||
|
|
|
@ -36,3 +36,9 @@ export const visLibVisTypeDefinitions = [
|
|||
gaugeVisTypeDefinition,
|
||||
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 { createVislibVisController, VislibVisController } from './vis_controller';
|
||||
import { VisTypeVislibCoreSetup } from './plugin';
|
||||
import { PieRenderValue } from './pie_fn';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
type VislibWrapperProps = VislibRenderValue & {
|
||||
type VislibWrapperProps = (VislibRenderValue | PieRenderValue) & {
|
||||
core: VisTypeVislibCoreSetup;
|
||||
charts: ChartsPluginSetup;
|
||||
handlers: IInterpreterRenderHandlers;
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// NOTE: Some of the styles attempt to align with the TSVB legend
|
||||
|
||||
$visLegendWidth: 150px;
|
||||
$visColorPickerWidth: $euiSizeM * 10;
|
||||
$visLegendLineHeight: $euiSize;
|
||||
|
||||
.visLegend__toggle {
|
||||
border-radius: $euiBorderRadius;
|
||||
|
@ -81,20 +79,3 @@ $visLegendLineHeight: $euiSize;
|
|||
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();
|
||||
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();
|
||||
first.simulate('click');
|
||||
|
||||
const popover = wrapper.find('.visLegend__valueDetails').first();
|
||||
const firstColor = popover.find('.visLegend__valueColorPickerDot').first();
|
||||
const popover = wrapper.find('.visColorPicker').first();
|
||||
const firstColor = popover.find('.visColorPicker__valueDot').first();
|
||||
firstColor.simulate('click');
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const colors = this.props.uiState?.get('vis.colors') || {};
|
||||
if (colors[label] === color) delete colors[label];
|
||||
else colors[label] = color;
|
||||
this.props.uiState?.setSilent('vis.colors', null);
|
||||
this.props.uiState?.set('vis.colors', colors);
|
||||
this.props.uiState?.emit('colorChanged');
|
||||
this.refresh();
|
||||
this.setState({ selectedLabel: null }, () => {
|
||||
const colors = this.props.uiState?.get('vis.colors') || {};
|
||||
if (colors[label] === color || !color) delete colors[label];
|
||||
else colors[label] = color;
|
||||
this.props.uiState?.setSilent('vis.colors', null);
|
||||
this.props.uiState?.set('vis.colors', colors);
|
||||
this.props.uiState?.emit('colorChanged');
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
filter = ({ values: data }: LegendItem, negate: boolean) => {
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
*/
|
||||
|
||||
import React, { memo, useState, BaseSyntheticEvent, KeyboardEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiPopover,
|
||||
keys,
|
||||
|
@ -33,7 +31,8 @@ import {
|
|||
EuiButtonGroupOptionProps,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { legendColors, LegendItem } from './models';
|
||||
import { LegendItem } from './models';
|
||||
import { ColorPicker } from '../../../../../charts/public';
|
||||
|
||||
interface Props {
|
||||
item: LegendItem;
|
||||
|
@ -45,7 +44,7 @@ interface Props {
|
|||
onSelect: (label: string | null) => (event?: BaseSyntheticEvent) => void;
|
||||
onHighlight: (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;
|
||||
}
|
||||
|
||||
|
@ -159,40 +158,14 @@ const VisLegendItemComponent = ({
|
|||
closePopover={onSelect(null)}
|
||||
panelPaddingSize="s"
|
||||
>
|
||||
<div className="visLegend__valueDetails">
|
||||
{canFilter && renderFilterBar()}
|
||||
{canFilter && renderFilterBar()}
|
||||
|
||||
<div className="visLegend__valueColorPicker" role="listbox">
|
||||
<span id={`${legendId}ColorPickerDesc`} className="euiScreenReaderOnly">
|
||||
<FormattedMessage
|
||||
id="visTypeVislib.vislib.legend.setColorScreenReaderDescription"
|
||||
defaultMessage="Set color for value {legendDataLabel}"
|
||||
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>
|
||||
<ColorPicker
|
||||
id={legendId}
|
||||
label={item.label}
|
||||
color={getColor(item.label)}
|
||||
onChange={(c, e) => setColor(item.label, c, e)}
|
||||
/>
|
||||
</EuiPopover>
|
||||
);
|
||||
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import type { Dimension } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { addToSiri, Serie } from './_add_to_siri';
|
||||
import { Point } from './_get_point';
|
||||
import { Dimension } from './point_series';
|
||||
|
||||
describe('addToSiri', function () {
|
||||
it('creates a new series the first time it sees an id', function () {
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { getAggId } from '../../../../../vis_type_xy/public';
|
||||
import type { Dimension } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { Point } from './_get_point';
|
||||
import { Dimension } from './point_series';
|
||||
|
||||
export interface Serie {
|
||||
id: string;
|
||||
|
@ -48,7 +50,7 @@ export function addToSiri(
|
|||
}
|
||||
|
||||
series.set(id, {
|
||||
id: id.split('-').pop() as string,
|
||||
id: getAggId(id),
|
||||
rawId: id,
|
||||
label: yLabel == null ? id : yLabel,
|
||||
count: 0,
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import type { Dimension, Dimensions } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { getAspects } from './_get_aspects';
|
||||
import { Dimension, Dimensions, Aspect } from './point_series';
|
||||
import { Aspect } from './point_series';
|
||||
import { Table, Row } from '../../types';
|
||||
|
||||
describe('getAspects', function () {
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import type { Dimensions } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { makeFakeXAspect } from './_fake_x_aspect';
|
||||
import { Dimensions, Aspects } from './point_series';
|
||||
import { Aspects } from './point_series';
|
||||
import { Table } from '../../types';
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { IFieldFormatsRegistry } from '../../../../../data/common';
|
||||
|
||||
import { getPoint } from './_get_point';
|
||||
import { setFormatService } from '../../../services';
|
||||
import { Aspect } from './point_series';
|
||||
|
@ -94,7 +95,12 @@ describe('getPoint', function () {
|
|||
|
||||
it('should call deserialize', function () {
|
||||
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);
|
||||
|
||||
|
|
|
@ -18,16 +18,12 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
import type { DateHistogramParams, HistogramParams } from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { initXAxis } from './_init_x_axis';
|
||||
import { makeFakeXAspect } from './_fake_x_aspect';
|
||||
import {
|
||||
Aspects,
|
||||
Chart,
|
||||
DateHistogramOrdered,
|
||||
DateHistogramParams,
|
||||
HistogramOrdered,
|
||||
HistogramParams,
|
||||
} from './point_series';
|
||||
import { Aspects, Chart, DateHistogramOrdered, HistogramOrdered } from './point_series';
|
||||
import { Table, Column } from '../../types';
|
||||
|
||||
describe('initXAxis', function () {
|
||||
|
@ -110,7 +106,7 @@ describe('initXAxis', function () {
|
|||
|
||||
it('reads the date interval param from the x agg', function () {
|
||||
const dateHistogramParams = chart.aspects.x[0].params as DateHistogramParams;
|
||||
dateHistogramParams.interval = 'P1D';
|
||||
dateHistogramParams.interval = moment.duration(1, 'd').asMilliseconds();
|
||||
dateHistogramParams.intervalESValue = 1;
|
||||
dateHistogramParams.intervalESUnit = 'd';
|
||||
dateHistogramParams.date = true;
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
|
||||
import type { DateHistogramParams } from '../../../../../vis_type_xy/public/types';
|
||||
|
||||
import { orderedDateAxis } from './_ordered_date_axis';
|
||||
import { DateHistogramParams, OrderedChart } from './point_series';
|
||||
import { OrderedChart } from './point_series';
|
||||
|
||||
describe('orderedDateAxis', function () {
|
||||
const baseArgs = {
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
*/
|
||||
|
||||
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 { setFormatService } from '../../../services';
|
||||
import { Serie } from './_add_to_siri';
|
||||
|
|
|
@ -18,6 +18,14 @@
|
|||
*/
|
||||
|
||||
import { Duration } from 'moment';
|
||||
|
||||
import type {
|
||||
Dimension,
|
||||
Dimensions,
|
||||
DateHistogramParams,
|
||||
HistogramParams,
|
||||
} from '../../../../../vis_type_xy/public';
|
||||
|
||||
import { getSeries } from './_get_series';
|
||||
import { getAspects } from './_get_aspects';
|
||||
import { initYAxis } from './_init_y_axis';
|
||||
|
@ -26,41 +34,6 @@ import { orderedDateAxis } from './_ordered_date_axis';
|
|||
import { Serie } from './_add_to_siri';
|
||||
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 {
|
||||
accessor: Column['id'];
|
||||
column?: Dimension['accessor'];
|
||||
|
|
|
@ -21,15 +21,16 @@ import d3 from 'd3';
|
|||
import _ from 'lodash';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
|
||||
import { dispatchRenderComplete } from '../../../../kibana_utils/public';
|
||||
|
||||
import { visTypes as chartTypes } from '../visualizations/vis_types';
|
||||
import { NoResults } from '../errors';
|
||||
import { Layout } from './layout/layout';
|
||||
import { ChartTitle } from './chart_title';
|
||||
import { Alerts } from './alerts';
|
||||
import { Axis } from './axis/axis';
|
||||
import { ChartGrid as Grid } from './chart_grid';
|
||||
import { visTypes as chartTypes } from '../visualizations/vis_types';
|
||||
import { Binder } from './binder';
|
||||
import { dispatchRenderComplete } from '../../../../kibana_utils/public';
|
||||
|
||||
const markdownIt = new MarkdownIt({
|
||||
html: false,
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
"params": {
|
||||
"date": true,
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"format": "YYYY-MM-DD",
|
||||
"bounds": {
|
||||
"min": "2019-05-10T04:00:00.000Z",
|
||||
|
@ -128,7 +128,7 @@
|
|||
},
|
||||
"xAxisLabel": "timestamp per day",
|
||||
"ordered": {
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"date": true,
|
||||
"min": 1557460800000,
|
||||
"max": 1557656337342
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
"params": {
|
||||
"date": true,
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"format": "YYYY-MM-DD",
|
||||
"bounds": {
|
||||
"min": "2019-05-10T04:00:00.000Z",
|
||||
|
@ -128,7 +128,7 @@
|
|||
},
|
||||
"xAxisLabel": "timestamp per day",
|
||||
"ordered": {
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"date": true,
|
||||
"min": 1557460800000,
|
||||
"max": 1557656337342
|
||||
|
@ -460,4 +460,4 @@
|
|||
"50th percentile of AvgTicketPrice"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"params": {
|
||||
"date": true,
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"format": "YYYY-MM-DD",
|
||||
"bounds": {
|
||||
"min": "2019-05-10T04:00:00.000Z",
|
||||
|
@ -453,4 +453,4 @@
|
|||
}
|
||||
],
|
||||
"enableHover": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"params": {
|
||||
"date": true,
|
||||
"interval": "P1D",
|
||||
"interval": 86400000,
|
||||
"format": "YYYY-MM-DD",
|
||||
"bounds": {
|
||||
"min": "2019-05-10T04:00:00.000Z",
|
||||
|
@ -455,4 +455,4 @@
|
|||
}
|
||||
],
|
||||
"enableHover": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import { Chart } from './_chart';
|
|||
import { gaugeTypes } from './gauges/gauge_types';
|
||||
|
||||
export class GaugeChart extends Chart {
|
||||
constructor(handler, chartEl, chartData, deps) {
|
||||
super(handler, chartEl, chartData, deps);
|
||||
constructor(handler, chartEl, chartData, uiSettings) {
|
||||
super(handler, chartEl, chartData, uiSettings);
|
||||
this.gaugeConfig = handler.visConfig.get('gauge', {});
|
||||
this.gauge = new gaugeTypes[this.gaugeConfig.type](this);
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ const defaults = {
|
|||
* @param chartData {Object} Elasticsearch query results for this specific chart
|
||||
*/
|
||||
export class HeatmapChart extends PointSeries {
|
||||
constructor(handler, chartEl, chartData, seriesConfigArgs, deps) {
|
||||
super(handler, chartEl, chartData, seriesConfigArgs, deps);
|
||||
constructor(handler, chartEl, chartData, seriesConfigArgs, core) {
|
||||
super(handler, chartEl, chartData, seriesConfigArgs, core);
|
||||
|
||||
this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults);
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import { UiSettingsParams } from 'kibana/server';
|
|||
import { DIMMING_OPACITY_SETTING, HEATMAP_MAX_BUCKETS_SETTING } from '../common';
|
||||
|
||||
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]: {
|
||||
name: i18n.translate('visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle', {
|
||||
defaultMessage: 'Dimming opacity',
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
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",
|
||||
"server": 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