=> ({
+ ...defaultProps,
+ stateParams: {
+ ...defaultProps.stateParams,
+ valueAxes: [
+ {
+ ...valueAxis,
+ id: 'ValueAxis-1',
+ position: mapPosition(position),
+ },
+ {
+ ...valueAxis,
+ id: 'ValueAxis-2',
+ position: mapPositionOpposite(mapPosition(position)),
+ },
+ {
+ ...valueAxis,
+ id: 'ValueAxis-3',
+ position: mapPosition(position),
+ },
+ ],
+ categoryAxes: [
+ {
+ ...categoryAxis,
+ position,
+ },
+ ],
+ },
+ });
+
+ it('should update all value axes if category axis changes from horizontal to vertical', () => {
+ const component = mount();
+ setValue.mockClear();
+ const onPositionChanged = component.find(CategoryAxisPanel).prop('onPositionChanged');
+ onPositionChanged(Position.Left);
+ expect(setValue).nthCalledWith(1, 'valueAxes', [
+ expect.objectContaining({
+ id: 'ValueAxis-1',
+ position: Position.Bottom,
+ }),
+ expect.objectContaining({
+ id: 'ValueAxis-2',
+ position: Position.Top,
+ }),
+ expect.objectContaining({
+ id: 'ValueAxis-3',
+ position: Position.Bottom,
+ }),
+ ]);
+ });
+
+ it('should update all value axes if category axis changes from vertical to horizontal', () => {
+ const component = mount();
+ setValue.mockClear();
+ const onPositionChanged = component.find(CategoryAxisPanel).prop('onPositionChanged');
+ onPositionChanged(Position.Top);
+ expect(setValue).nthCalledWith(1, 'valueAxes', [
+ expect.objectContaining({
+ id: 'ValueAxis-1',
+ position: Position.Left,
+ }),
+ expect.objectContaining({
+ id: 'ValueAxis-2',
+ position: Position.Right,
+ }),
+ expect.objectContaining({
+ id: 'ValueAxis-3',
+ position: Position.Left,
+ }),
+ ]);
+ });
+
+ it('should not update value axes if category axis stays horizontal', () => {
+ const component = mount();
+ setValue.mockClear();
+ const onPositionChanged = component.find(CategoryAxisPanel).prop('onPositionChanged');
+ onPositionChanged(Position.Top);
+ expect(setValue).not.toBeCalled();
+ });
+
+ it('should not update value axes if category axis stays vertical', () => {
+ const component = mount();
+ setValue.mockClear();
+ const onPositionChanged = component.find(CategoryAxisPanel).prop('onPositionChanged');
+ onPositionChanged(Position.Right);
+ expect(setValue).not.toBeCalled();
+ });
+ });
+});
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx
similarity index 83%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx
index 0862c47c35cff5..9b4bf3127d45e1 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/index.tsx
@@ -19,10 +19,12 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { cloneDeep, get } from 'lodash';
+
import { EuiSpacer } from '@elastic/eui';
-import { IAggConfig } from 'src/plugins/data/public';
-import { BasicVislibParams, ValueAxis, SeriesParam, Axis } from '../../../types';
+import { IAggConfig } from '../../../../../../data/public';
+
+import { VisParams, ValueAxis, SeriesParam, CategoryAxis } from '../../../../types';
import { ValidationVisOptionsProps } from '../../common';
import { SeriesPanel } from './series_panel';
import { CategoryAxisPanel } from './category_axis_panel';
@@ -34,6 +36,7 @@ import {
getUpdatedAxisName,
mapPositionOpposite,
mapPosition,
+ mapPositionOpposingOpposite,
} from './utils';
export type SetParamByIndex = (
@@ -51,11 +54,9 @@ export type ChangeValueAxis = (
const VALUE_AXIS_PREFIX = 'ValueAxis-';
-function MetricsAxisOptions(props: ValidationVisOptionsProps) {
+function MetricsAxisOptions(props: ValidationVisOptionsProps) {
const { stateParams, setValue, aggs, vis, isTabSelected } = props;
- const [isCategoryAxisHorizontal, setIsCategoryAxisHorizontal] = useState(true);
-
const setParamByIndex: SetParamByIndex = useCallback(
(axesName, index, paramName, value) => {
const items = stateParams[axesName];
@@ -72,7 +73,7 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps)
);
const setCategoryAxis = useCallback(
- (value: Axis) => {
+ (value: CategoryAxis) => {
const categoryAxes = [...stateParams.categoryAxes];
categoryAxes[0] = value;
setValue('categoryAxes', categoryAxes);
@@ -170,33 +171,58 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps)
);
const onValueAxisPositionChanged = useCallback(
- (index: number, value: ValueAxis['position']) => {
+ (index: number, axisPosition: ValueAxis['position']) => {
+ const isHorizontalAxis = isAxisHorizontal(axisPosition);
const valueAxes = [...stateParams.valueAxes];
- const name = getUpdatedAxisName(value, valueAxes);
+ const name = getUpdatedAxisName(axisPosition, valueAxes);
+ const [categoryAxes] = stateParams.categoryAxes;
- valueAxes[index] = {
- ...valueAxes[index],
- name,
- position: value,
- };
- setValue('valueAxes', valueAxes);
+ if (isAxisHorizontal(categoryAxes.position) === isHorizontalAxis) {
+ const updatedCategoryAxes = {
+ ...categoryAxes,
+ position: mapPosition(categoryAxes.position),
+ };
+
+ setValue('categoryAxes', [updatedCategoryAxes]);
+
+ const oldPosition = valueAxes[index].position;
+ const newValueAxes = valueAxes.map(({ position, ...axis }, i) => ({
+ ...axis,
+ position:
+ i === index
+ ? axisPosition
+ : mapPositionOpposingOpposite(position, oldPosition, axisPosition),
+ }));
+ setValue('valueAxes', newValueAxes);
+ } else {
+ valueAxes[index] = {
+ ...valueAxes[index],
+ name,
+ position: axisPosition,
+ };
+ setValue('valueAxes', valueAxes);
+ }
},
- [stateParams.valueAxes, setValue]
+ [stateParams.valueAxes, stateParams.categoryAxes, setValue]
);
const onCategoryAxisPositionChanged = useCallback(
- (chartPosition: Axis['position']) => {
- const isChartHorizontal = isAxisHorizontal(chartPosition);
- setIsCategoryAxisHorizontal(isAxisHorizontal(chartPosition));
-
- stateParams.valueAxes.forEach((axis, index) => {
- if (isAxisHorizontal(axis.position) === isChartHorizontal) {
- const position = mapPosition(axis.position);
- onValueAxisPositionChanged(index, position);
- }
- });
+ (axisPosition: CategoryAxis['position']) => {
+ const isHorizontalAxis = isAxisHorizontal(axisPosition);
+
+ if (
+ stateParams.valueAxes.some(
+ ({ position }) => isAxisHorizontal(position) === isHorizontalAxis
+ )
+ ) {
+ const newValueAxes = stateParams.valueAxes.map(({ position, ...axis }) => ({
+ ...axis,
+ position: mapPosition(position),
+ }));
+ setValue('valueAxes', newValueAxes);
+ }
},
- [stateParams.valueAxes, onValueAxisPositionChanged]
+ [setValue, stateParams.valueAxes]
);
const addValueAxis = useCallback(() => {
@@ -305,7 +331,6 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps)
(
- paramName: T,
- value: Axis['labels'][T]
-) => void;
+export type SetAxisLabel = (paramName: T, value: Labels[T]) => void;
export interface LabelOptionsProps {
- axisLabels: Axis['labels'];
+ axisLabels: Labels;
axisFilterCheckboxName: string;
setAxisLabel: SetAxisLabel;
}
function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: LabelOptionsProps) {
const setAxisLabelRotate = useCallback(
- (paramName: 'rotate', value: Axis['labels']['rotate']) => {
+ (paramName: 'rotate', value: Labels['rotate']) => {
setAxisLabel(paramName, Number(value));
},
[setAxisLabel]
@@ -54,7 +52,7 @@ function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: Labe
@@ -62,7 +60,7 @@ function LabelOptions({ axisLabels, axisFilterCheckboxName, setAxisLabel }: Labe
@@ -57,13 +58,10 @@ function SeriesPanel({ seriesParams, ...chartProps }: SeriesPanelProps) {
initialIsOpen={index === 0}
buttonContent={chart.data.label}
buttonContentClassName="visEditorSidebar__aggGroupAccordionButtonContent eui-textTruncate"
- aria-label={i18n.translate(
- 'visTypeVislib.controls.pointSeries.seriesAccordionAriaLabel',
- {
- defaultMessage: 'Toggle {agg} options',
- values: { agg: chart.data.label },
- }
- )}
+ aria-label={i18n.translate('visTypeXy.controls.pointSeries.seriesAccordionAriaLabel', {
+ defaultMessage: 'Toggle {agg} options',
+ values: { agg: chart.data.label },
+ })}
>
<>
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/utils.ts
similarity index 53%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/utils.ts
index 708e8cf15f0296..58216ee8953cdb 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/utils.ts
@@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-
import { upperFirst } from 'lodash';
-import { BasicVislibParams, ValueAxis, SeriesParam } from '../../../types';
-import { ChartModes, ChartTypes, InterpolationModes, Positions } from '../../../utils/collections';
+import { Position } from '@elastic/charts';
+
+import { VisParams, ValueAxis, SeriesParam, ChartMode, InterpolationMode } from '../../../../types';
+import { ChartType } from '../../../../../common';
-const makeSerie = (
+export const makeSerie = (
id: string,
label: string,
defaultValueAxis: ValueAxis['id'],
@@ -31,11 +32,11 @@ const makeSerie = (
const data = { id, label };
const defaultSerie = {
show: true,
- mode: ChartModes.NORMAL,
- type: ChartTypes.LINE,
+ mode: ChartMode.Normal,
+ type: ChartType.Line,
drawLinesBetweenPoints: true,
showCircles: true,
- interpolate: InterpolationModes.LINEAR,
+ interpolate: InterpolationMode.Linear,
lineWidth: 2,
valueAxis: defaultValueAxis,
data,
@@ -43,12 +44,12 @@ const makeSerie = (
return lastSerie ? { ...lastSerie, data } : defaultSerie;
};
-const isAxisHorizontal = (position: Positions) =>
- [Positions.TOP, Positions.BOTTOM].includes(position as any);
+export const isAxisHorizontal = (position: Position) =>
+ [Position.Top, Position.Bottom].includes(position as any);
const RADIX = 10;
-function countNextAxisNumber(axisName: string, axisProp: 'id' | 'name' = 'id') {
+export function countNextAxisNumber(axisName: string, axisProp: 'id' | 'name' = 'id') {
return (value: number, axis: ValueAxis) => {
const nameLength = axisName.length;
if (axis[axisProp].substr(0, nameLength) === axisName) {
@@ -63,9 +64,9 @@ function countNextAxisNumber(axisName: string, axisProp: 'id' | 'name' = 'id') {
const AXIS_PREFIX = 'Axis-';
-const getUpdatedAxisName = (
+export const getUpdatedAxisName = (
axisPosition: ValueAxis['position'],
- valueAxes: BasicVislibParams['valueAxes']
+ valueAxes: VisParams['valueAxes']
) => {
const axisName = upperFirst(axisPosition) + AXIS_PREFIX;
const nextAxisNameNumber = valueAxes.reduce(countNextAxisNumber(axisName, 'name'), 1);
@@ -73,39 +74,56 @@ const getUpdatedAxisName = (
return `${axisName}${nextAxisNameNumber}`;
};
-function mapPositionOpposite(position: Positions) {
+/**
+ * Maps axis position to opposite position
+ * @param position
+ */
+export function mapPositionOpposite(position: Position) {
switch (position) {
- case Positions.BOTTOM:
- return Positions.TOP;
- case Positions.TOP:
- return Positions.BOTTOM;
- case Positions.LEFT:
- return Positions.RIGHT;
- case Positions.RIGHT:
- return Positions.LEFT;
+ case Position.Bottom:
+ return Position.Top;
+ case Position.Top:
+ return Position.Bottom;
+ case Position.Left:
+ return Position.Right;
+ case Position.Right:
+ return Position.Left;
default:
throw new Error('Invalid legend position.');
}
}
-function mapPosition(position: Positions) {
- switch (position) {
- case Positions.BOTTOM:
- return Positions.LEFT;
- case Positions.TOP:
- return Positions.RIGHT;
- case Positions.LEFT:
- return Positions.BOTTOM;
- case Positions.RIGHT:
- return Positions.TOP;
+/**
+ * Maps axis position to new position or opposite of new position based on old position
+ * @param position
+ * @param oldPosition
+ * @param newPosition
+ */
+export function mapPositionOpposingOpposite(
+ position: Position,
+ oldPosition: Position,
+ newPosition: Position
+) {
+ if (position === oldPosition) {
+ return newPosition;
}
+
+ return mapPositionOpposite(newPosition);
}
-export {
- makeSerie,
- isAxisHorizontal,
- countNextAxisNumber,
- getUpdatedAxisName,
- mapPositionOpposite,
- mapPosition,
-};
+/**
+ * Maps axis position to opposite rotation position
+ * @param position
+ */
+export function mapPosition(position: Position) {
+ switch (position) {
+ case Position.Bottom:
+ return Position.Left;
+ case Position.Top:
+ return Position.Right;
+ case Position.Left:
+ return Position.Bottom;
+ case Position.Right:
+ return Position.Top;
+ }
+}
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx
similarity index 96%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx
index 070433ca03f6da..f3286029db32f1 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.test.tsx
@@ -19,10 +19,12 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { ValueAxesPanel, ValueAxesPanelProps } from './value_axes_panel';
-import { ValueAxis, SeriesParam } from '../../../types';
-import { Positions } from '../../../utils/collections';
+
import { mountWithIntl } from '@kbn/test/jest';
+import { Position } from '@elastic/charts';
+
+import { ValueAxis, SeriesParam } from '../../../../types';
+import { ValueAxesPanel, ValueAxesPanelProps } from './value_axes_panel';
import { valueAxis, seriesParam, vis } from './mocks';
describe('ValueAxesPanel component', () => {
@@ -47,7 +49,7 @@ describe('ValueAxesPanel component', () => {
axisRight = {
...valueAxis,
id: 'ValueAxis-2',
- position: Positions.RIGHT,
+ position: Position.Right,
};
seriesParamCount = { ...seriesParam };
seriesParamAverage = {
@@ -63,7 +65,6 @@ describe('ValueAxesPanel component', () => {
seriesParams: [seriesParamCount, seriesParamAverage],
valueAxes: [axisLeft, axisRight],
vis,
- isCategoryAxisHorizontal: false,
setParamByIndex,
onValueAxisPositionChanged,
addValueAxis,
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx
similarity index 90%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx
index 5f7d33b7f1f478..397704663ff1f7 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axes_panel.tsx
@@ -31,13 +31,13 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { Vis } from '../../../../../visualizations/public';
-import { SeriesParam, ValueAxis } from '../../../types';
+import { Vis } from '../../../../../../visualizations/public';
+
+import { SeriesParam, ValueAxis } from '../../../../types';
import { ValueAxisOptions } from './value_axis_options';
import { SetParamByIndex } from '.';
export interface ValueAxesPanelProps {
- isCategoryAxisHorizontal: boolean;
addValueAxis: () => ValueAxis;
removeValueAxis: (axis: ValueAxis) => void;
onValueAxisPositionChanged: (index: number, value: ValueAxis['position']) => void;
@@ -64,7 +64,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
const removeButtonTooltip = useMemo(
() =>
- i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.removeButtonTooltip', {
+ i18n.translate('visTypeXy.controls.pointSeries.valueAxes.removeButtonTooltip', {
defaultMessage: 'Remove Y-axis',
}),
[]
@@ -87,7 +87,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
const addButtonTooltip = useMemo(
() =>
- i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.addButtonTooltip', {
+ i18n.translate('visTypeXy.controls.pointSeries.valueAxes.addButtonTooltip', {
defaultMessage: 'Add Y-axis',
}),
[]
@@ -115,7 +115,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
@@ -146,7 +146,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
buttonClassName="eui-textTruncate"
buttonContentClassName="visEditorSidebar__aggGroupAccordionButtonContent eui-textTruncate"
aria-label={i18n.translate(
- 'visTypeVislib.controls.pointSeries.valueAxes.toggleOptionsAriaLabel',
+ 'visTypeXy.controls.pointSeries.valueAxes.toggleOptionsAriaLabel',
{
defaultMessage: 'Toggle {axisName} options',
values: { axisName: axis.name },
@@ -160,7 +160,6 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
axis={axis}
index={index}
valueAxis={valueAxes[index]}
- isCategoryAxisHorizontal={props.isCategoryAxisHorizontal}
onValueAxisPositionChanged={props.onValueAxisPositionChanged}
setParamByIndex={props.setParamByIndex}
setMultipleValidity={props.setMultipleValidity}
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx
similarity index 64%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx
index 617410b091fd97..62757d14a01961 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx
@@ -19,21 +19,18 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { ValueAxisOptions, ValueAxisOptionsParams } from './value_axis_options';
-import { ValueAxis } from '../../../types';
-import { TextInputOption } from '../../../../../vis_default_editor/public';
+
+import { Position } from '@elastic/charts';
+
+import { TextInputOption } from '../../../../../../vis_default_editor/public';
+
+import { ValueAxis, ScaleType } from '../../../../types';
import { LabelOptions } from './label_options';
-import { ScaleTypes, Positions } from '../../../utils/collections';
+import { ValueAxisOptions, ValueAxisOptionsParams } from './value_axis_options';
import { valueAxis, vis } from './mocks';
const POSITION = 'position';
-interface PositionOption {
- text: string;
- value: Positions;
- disabled: boolean;
-}
-
describe('ValueAxisOptions component', () => {
let setParamByIndex: jest.Mock;
let onValueAxisPositionChanged: jest.Mock;
@@ -52,7 +49,6 @@ describe('ValueAxisOptions component', () => {
index: 0,
valueAxis,
vis,
- isCategoryAxisHorizontal: false,
setParamByIndex,
onValueAxisPositionChanged,
setMultipleValidity,
@@ -73,50 +69,8 @@ describe('ValueAxisOptions component', () => {
expect(comp.find(LabelOptions).exists()).toBeFalsy();
});
- it('should only allow left and right value axis position when category axis is horizontal', () => {
- defaultProps.isCategoryAxisHorizontal = true;
- const comp = shallow();
-
- const options: PositionOption[] = comp.find({ paramName: POSITION }).prop('options');
-
- expect(options.length).toBe(4);
- options.forEach(({ value, disabled }) => {
- switch (value) {
- case Positions.LEFT:
- case Positions.RIGHT:
- expect(disabled).toBeFalsy();
- break;
- case Positions.TOP:
- case Positions.BOTTOM:
- expect(disabled).toBeTruthy();
- break;
- }
- });
- });
-
- it('should only allow top and bottom value axis position when category axis is vertical', () => {
- defaultProps.isCategoryAxisHorizontal = false;
- const comp = shallow();
-
- const options: PositionOption[] = comp.find({ paramName: POSITION }).prop('options');
-
- expect(options.length).toBe(4);
- options.forEach(({ value, disabled }) => {
- switch (value) {
- case Positions.LEFT:
- case Positions.RIGHT:
- expect(disabled).toBeTruthy();
- break;
- case Positions.TOP:
- case Positions.BOTTOM:
- expect(disabled).toBeFalsy();
- break;
- }
- });
- });
-
it('should call onValueAxisPositionChanged when position is changed', () => {
- const value = Positions.RIGHT;
+ const value = Position.Right;
const comp = shallow();
comp.find({ paramName: POSITION }).prop('setValue')(POSITION, value);
@@ -135,7 +89,7 @@ describe('ValueAxisOptions component', () => {
});
it('should call setValueAxis when scale value is changed', () => {
- const scaleValue = ScaleTypes.SQUARE_ROOT;
+ const scaleValue = ScaleType.SquareRoot;
const comp = shallow();
comp.find({ paramName: 'type' }).prop('setValue')('type', scaleValue);
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx
similarity index 76%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx
index 2d6868f5166eda..d81ddcb95ce623 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx
@@ -17,21 +17,20 @@
* under the License.
*/
-import React, { useCallback, useMemo } from 'react';
+import React, { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSpacer, EuiAccordion, EuiHorizontalRule } from '@elastic/eui';
-import { Vis } from '../../../../../visualizations/public';
-import { ValueAxis } from '../../../types';
-import { Positions } from '../../../utils/collections';
+import { Vis } from '../../../../../../visualizations/public';
import {
SelectOption,
SwitchOption,
TextInputOption,
-} from '../../../../../vis_default_editor/public';
+} from '../../../../../../vis_default_editor/public';
+
+import { ValueAxis } from '../../../../types';
import { LabelOptions, SetAxisLabel } from './label_options';
import { CustomExtentsOptions } from './custom_extents_options';
-import { isAxisHorizontal } from './utils';
import { SetParamByIndex } from '.';
export type SetScale = (
@@ -42,7 +41,6 @@ export type SetScale = (
export interface ValueAxisOptionsParams {
axis: ValueAxis;
index: number;
- isCategoryAxisHorizontal: boolean;
onValueAxisPositionChanged: (index: number, value: ValueAxis['position']) => void;
setParamByIndex: SetParamByIndex;
valueAxis: ValueAxis;
@@ -50,10 +48,9 @@ export interface ValueAxisOptionsParams {
setMultipleValidity: (paramName: string, isValid: boolean) => void;
}
-function ValueAxisOptions({
+export function ValueAxisOptions({
axis,
index,
- isCategoryAxisHorizontal,
valueAxis,
vis,
onValueAxisPositionChanged,
@@ -109,34 +106,13 @@ function ValueAxisOptions({
[index, onValueAxisPositionChanged]
);
- const isPositionDisabled = useCallback(
- (position: Positions) => {
- if (isCategoryAxisHorizontal) {
- return isAxisHorizontal(position);
- }
- return [Positions.LEFT, Positions.RIGHT].includes(position as any);
- },
- [isCategoryAxisHorizontal]
- );
-
- const positions = useMemo(
- () =>
- vis.type.editorConfig.collections.positions.map(
- (position: { text: string; value: Positions }) => ({
- ...position,
- disabled: isPositionDisabled(position.value),
- })
- ),
- [vis.type.editorConfig.collections.positions, isPositionDisabled]
- );
-
return (
<>
);
}
-
-export { ValueAxisOptions };
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx
similarity index 93%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx
index a6b8e606e09a60..27a28d96d06086 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx
@@ -19,9 +19,10 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
+
+import { ScaleType } from '../../../../types';
import { YExtents, YExtentsProps } from './y_extents';
-import { ScaleTypes } from '../../../utils/collections';
-import { NumberInputOption } from '../../../../../vis_default_editor/public';
+import { NumberInputOption } from '../../../../../../vis_default_editor/public';
describe('YExtents component', () => {
let setMultipleValidity: jest.Mock;
@@ -35,7 +36,7 @@ describe('YExtents component', () => {
defaultProps = {
scale: {
- type: ScaleTypes.LINEAR,
+ type: ScaleType.Linear,
},
setMultipleValidity,
setScale,
@@ -81,7 +82,7 @@ describe('YExtents component', () => {
it('should call setMultipleValidity with false when min equals 0 and scale is log', () => {
defaultProps.scale.min = 0;
defaultProps.scale.max = 1;
- defaultProps.scale.type = ScaleTypes.LOG;
+ defaultProps.scale.type = ScaleType.Log;
mount();
expect(setMultipleValidity).toBeCalledWith(Y_EXTENTS, false);
diff --git a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx
similarity index 83%
rename from src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx
index 1e7c2bc844e446..ba7049e9845738 100644
--- a/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx
@@ -21,15 +21,15 @@ import React, { useEffect, useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { Scale } from '../../../types';
-import { ScaleTypes } from '../../../utils/collections';
-import { NumberInputOption } from '../../../../../vis_default_editor/public';
+import { NumberInputOption } from '../../../../../../vis_default_editor/public';
+
+import { Scale, ScaleType } from '../../../../types';
import { SetScale } from './value_axis_options';
-const rangeError = i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.minErrorMessage', {
+const rangeError = i18n.translate('visTypeXy.controls.pointSeries.valueAxes.minErrorMessage', {
defaultMessage: 'Min should be less than Max.',
});
-const minError = i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.minNeededScaleText', {
+const minError = i18n.translate('visTypeXy.controls.pointSeries.valueAxes.minNeededScaleText', {
defaultMessage: 'Min must exceed 0 when a log scale is selected.',
});
@@ -59,7 +59,7 @@ function YExtents({ scale, setScale, setMultipleValidity }: YExtentsProps) {
errors.push(rangeError);
}
- if (type === ScaleTypes.LOG && (isNullOrUndefined(min) || min <= 0)) {
+ if (type === ScaleType.Log && (isNullOrUndefined(min) || min <= 0)) {
errors.push(minError);
}
@@ -86,7 +86,7 @@ function YExtents({ scale, setScale, setMultipleValidity }: YExtentsProps) {
) {
+ const { stateParams, setValue, vis, aggs } = props;
+
+ const hasLineChart = stateParams.seriesParams.some(
+ ({ type, data: { id: paramId } }) =>
+ (type === ChartType.Line || type === ChartType.Area) &&
+ aggs.aggs.find(({ id }) => id === paramId)?.enabled
+ );
+
+ return (
+ <>
+
+
+ {hasLineChart && (
+
+ )}
+ >
+ );
+}
diff --git a/src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx
similarity index 59%
rename from src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx
index 0d831f005bd34b..9efc9b65b19ee0 100644
--- a/src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx
@@ -16,28 +16,36 @@
* specific language governing permissions and limitations
* under the License.
*/
+
import React, { useMemo, useEffect, useCallback } from 'react';
-import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
-import {
- SelectOption,
- SwitchOption,
- VisOptionsProps,
-} from '../../../../../vis_default_editor/public';
-import { BasicVislibParams, ValueAxis } from '../../../types';
+import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public';
+import { VisParams, ValueAxis } from '../../../../types';
+import { ValidationVisOptionsProps } from '../../common';
-function GridPanel({ stateParams, setValue, hasHistogramAgg }: VisOptionsProps) {
+type GridPanelOptions = ValidationVisOptionsProps<
+ VisParams,
+ {
+ showElasticChartsOptions: boolean;
+ }
+>;
+
+function GridPanel({ stateParams, setValue, hasHistogramAgg, extraProps }: GridPanelOptions) {
const setGrid = useCallback(
- (
- paramName: T,
- value: BasicVislibParams['grid'][T]
- ) => setValue('grid', { ...stateParams.grid, [paramName]: value }),
+ (paramName: T, value: VisParams['grid'][T]) =>
+ setValue('grid', { ...stateParams.grid, [paramName]: value }),
[stateParams.grid, setValue]
);
+ const disableCategoryGridLines = useMemo(
+ () => !extraProps?.showElasticChartsOptions && hasHistogramAgg,
+ [extraProps?.showElasticChartsOptions, hasHistogramAgg]
+ );
+
const options = useMemo(
() => [
...stateParams.valueAxes.map(({ id, name }: ValueAxis) => ({
@@ -45,7 +53,7 @@ function GridPanel({ stateParams, setValue, hasHistogramAgg }: VisOptionsProps {
- if (hasHistogramAgg) {
+ if (disableCategoryGridLines) {
setGrid('categoryLines', false);
}
- }, [hasHistogramAgg, setGrid]);
+ }, [disableCategoryGridLines, setGrid]);
return (
@@ -74,19 +82,16 @@ function GridPanel({ stateParams, setValue, hasHistogramAgg }: VisOptionsProps
diff --git a/src/plugins/vis_type_vislib/public/components/options/point_series/index.ts b/src/plugins/vis_type_xy/public/editor/components/options/point_series/index.ts
similarity index 100%
rename from src/plugins/vis_type_vislib/public/components/options/point_series/index.ts
rename to src/plugins/vis_type_xy/public/editor/components/options/point_series/index.ts
diff --git a/src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx
similarity index 63%
rename from src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx
index 59c591a9f55825..1d00f80e0b0d71 100644
--- a/src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx
@@ -16,25 +16,41 @@
* specific language governing permissions and limitations
* under the License.
*/
+
import React, { useMemo } from 'react';
import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { uniq } from 'lodash';
-import { ValidationVisOptionsProps } from '../../common';
-import { BasicOptions, SwitchOption } from '../../../../../vis_default_editor/public';
+import { BasicOptions, SwitchOption } from '../../../../../../vis_default_editor/public';
+import { BUCKET_TYPES } from '../../../../../../data/public';
+
+import { VisParams } from '../../../../types';
import { GridPanel } from './grid_panel';
import { ThresholdPanel } from './threshold_panel';
-import { BasicVislibParams } from '../../../types';
-import { ChartTypes } from '../../../utils/collections';
-
-function PointSeriesOptions(props: ValidationVisOptionsProps) {
- const { stateParams, setValue, vis } = props;
+import { ChartType } from '../../../../../common';
+import { ValidationVisOptionsProps } from '../../common';
+import { ElasticChartsOptions } from './elastic_charts_options';
- const currentChartTypes = useMemo(() => uniq(stateParams.seriesParams.map(({ type }) => type)), [
- stateParams.seriesParams,
- ]);
+export function PointSeriesOptions(
+ props: ValidationVisOptionsProps<
+ VisParams,
+ {
+ // TODO: Remove when vis_type_vislib is removed
+ // https://github.com/elastic/kibana/issues/56143
+ showElasticChartsOptions: boolean;
+ }
+ >
+) {
+ const { stateParams, setValue, vis, aggs } = props;
+ const hasBarChart = useMemo(
+ () =>
+ stateParams.seriesParams.some(
+ ({ type, data: { id: paramId } }) =>
+ type === ChartType.Histogram && aggs.aggs.find(({ id }) => id === paramId)?.enabled
+ ),
+ [stateParams.seriesParams, aggs.aggs]
+ );
return (
<>
@@ -42,7 +58,7 @@ function PointSeriesOptions(props: ValidationVisOptionsProps)
@@ -52,10 +68,11 @@ function PointSeriesOptions(props: ValidationVisOptionsProps)
{vis.data.aggs!.aggs.some(
- (agg) => agg.schema === 'segment' && agg.type.name === 'date_histogram'
+ (agg) => agg.schema === 'segment' && agg.type.name === BUCKET_TYPES.DATE_HISTOGRAM
) ? (
)
/>
) : (
)
/>
)}
- {currentChartTypes.includes(ChartTypes.HISTOGRAM) && (
+ {hasBarChart && (
)
}
/>
)}
+
+ {props.extraProps?.showElasticChartsOptions && }
@@ -98,5 +118,3 @@ function PointSeriesOptions(props: ValidationVisOptionsProps)
>
);
}
-
-export { PointSeriesOptions };
diff --git a/src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx
similarity index 77%
rename from src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx
rename to src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx
index 79e93be5aca373..8eab0c478e67b7 100644
--- a/src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx
+++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx
@@ -17,39 +17,40 @@
* under the License.
*/
import React, { useCallback } from 'react';
-import { EuiPanel, EuiTitle, EuiColorPicker, EuiFormRow, EuiSpacer } from '@elastic/eui';
+
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiPanel, EuiTitle, EuiColorPicker, EuiFormRow, EuiSpacer } from '@elastic/eui';
-import { ValidationVisOptionsProps } from '../../common';
import {
SelectOption,
SwitchOption,
RequiredNumberInputOption,
-} from '../../../../../vis_default_editor/public';
-import { BasicVislibParams } from '../../../types';
+} from '../../../../../../vis_default_editor/public';
+import { ValidationVisOptionsProps } from '../../common';
+import { VisParams } from '../../../../types';
function ThresholdPanel({
stateParams,
setValue,
setMultipleValidity,
vis,
-}: ValidationVisOptionsProps) {
+}: ValidationVisOptionsProps) {
const setThresholdLine = useCallback(
- (
+ (
paramName: T,
- value: BasicVislibParams['thresholdLine'][T]
+ value: VisParams['thresholdLine'][T]
) => setValue('thresholdLine', { ...stateParams.thresholdLine, [paramName]: value }),
[stateParams.thresholdLine, setValue]
);
const setThresholdLineColor = useCallback(
- (value: BasicVislibParams['thresholdLine']['color']) => setThresholdLine('color', value),
+ (value: VisParams['thresholdLine']['color']) => setThresholdLine('color', value),
[setThresholdLine]
);
const setThresholdLineValidity = useCallback(
- (paramName: keyof BasicVislibParams['thresholdLine'], isValid: boolean) =>
+ (paramName: keyof VisParams['thresholdLine'], isValid: boolean) =>
setMultipleValidity(`thresholdLine__${paramName}`, isValid),
[setMultipleValidity]
);
@@ -59,7 +60,7 @@ function ThresholdPanel({
@@ -67,7 +68,7 @@ function ThresholdPanel({
[
+ {
+ text: i18n.translate('visTypeXy.legendPositions.topText', {
+ defaultMessage: 'Top',
+ }),
+ value: Position.Top,
+ },
+ {
+ text: i18n.translate('visTypeXy.legendPositions.leftText', {
+ defaultMessage: 'Left',
+ }),
+ value: Position.Left,
+ },
+ {
+ text: i18n.translate('visTypeXy.legendPositions.rightText', {
+ defaultMessage: 'Right',
+ }),
+ value: Position.Right,
+ },
+ {
+ text: i18n.translate('visTypeXy.legendPositions.bottomText', {
+ defaultMessage: 'Bottom',
+ }),
+ value: Position.Bottom,
+ },
+];
diff --git a/src/plugins/vis_type_xy/public/editor/scale_types.ts b/src/plugins/vis_type_xy/public/editor/scale_types.ts
new file mode 100644
index 00000000000000..c115b04ead5fd8
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/editor/scale_types.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { ScaleType } from '../types';
+
+export const getScaleTypes = () => [
+ {
+ text: i18n.translate('visTypeXy.scaleTypes.linearText', {
+ defaultMessage: 'Linear',
+ }),
+ value: ScaleType.Linear,
+ },
+ {
+ text: i18n.translate('visTypeXy.scaleTypes.logText', {
+ defaultMessage: 'Log',
+ }),
+ value: ScaleType.Log,
+ },
+ {
+ text: i18n.translate('visTypeXy.scaleTypes.squareRootText', {
+ defaultMessage: 'Square root',
+ }),
+ value: ScaleType.SquareRoot,
+ },
+];
diff --git a/src/plugins/vis_type_xy/public/index.ts b/src/plugins/vis_type_xy/public/index.ts
index 9af75ce9059e92..0739ad77a245bc 100644
--- a/src/plugins/vis_type_xy/public/index.ts
+++ b/src/plugins/vis_type_xy/public/index.ts
@@ -17,11 +17,35 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../core/public';
import { VisTypeXyPlugin as Plugin } from './plugin';
export { VisTypeXyPluginSetup } from './plugin';
-export function plugin(initializerContext: PluginInitializerContext) {
- return new Plugin(initializerContext);
+// TODO: Remove when vis_type_vislib is removed
+// https://github.com/elastic/kibana/issues/56143
+export {
+ CategoryAxis,
+ ThresholdLine,
+ ValueAxis,
+ Grid,
+ SeriesParam,
+ Dimension,
+ Dimensions,
+ ScaleType,
+ AxisType,
+ HistogramParams,
+ DateHistogramParams,
+} from './types';
+export type { ValidationVisOptionsProps } from './editor/components/common/validation_wrapper';
+export { TruncateLabelsOption } from './editor/components/common/truncate_labels';
+export { getPositions } from './editor/positions';
+export { getScaleTypes } from './editor/scale_types';
+export { xyVisTypes } from './vis_types';
+export { getAggId } from './config/get_agg_id';
+
+// Export common types
+export * from '../common';
+
+export function plugin() {
+ return new Plugin();
}
diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts
index 667018c1e6e307..c8812b07e59494 100644
--- a/src/plugins/vis_type_xy/public/plugin.ts
+++ b/src/plugins/vis_type_xy/public/plugin.ts
@@ -17,25 +17,30 @@
* under the License.
*/
-import {
- CoreSetup,
- CoreStart,
- Plugin,
- IUiSettingsClient,
- PluginInitializerContext,
-} from 'kibana/public';
-
+import { CoreSetup, CoreStart, Plugin } from '../../../core/public';
import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public';
import { ChartsPluginSetup } from '../../charts/public';
+import { DataPublicPluginStart } from '../../data/public';
-export interface VisTypeXyDependencies {
- uiSettings: IUiSettingsClient;
- charts: ChartsPluginSetup;
-}
+import { createVisTypeXyVisFn } from './xy_vis_fn';
+import {
+ setDataActions,
+ setFormatService,
+ setThemeService,
+ setColorsService,
+ setTimefilter,
+ setUISettings,
+ setDocLinks,
+} from './services';
+import { visTypesDefinitions } from './vis_types';
+import { CHARTS_LIBRARY } from '../common';
+import { xyVisRenderer } from './vis_renderer';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface VisTypeXyPluginSetup {}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface VisTypeXyPluginStart {}
/** @internal */
export interface VisTypeXyPluginSetupDependencies {
@@ -48,40 +53,43 @@ export interface VisTypeXyPluginSetupDependencies {
export interface VisTypeXyPluginStartDependencies {
expressions: ReturnType;
visualizations: VisualizationsStart;
+ data: DataPublicPluginStart;
}
-type VisTypeXyCoreSetup = CoreSetup;
+type VisTypeXyCoreSetup = CoreSetup;
/** @internal */
-export class VisTypeXyPlugin implements Plugin {
- constructor(public initializerContext: PluginInitializerContext) {}
-
+export class VisTypeXyPlugin
+ implements
+ Plugin<
+ VisTypeXyPluginSetup,
+ VisTypeXyPluginStart,
+ VisTypeXyPluginSetupDependencies,
+ VisTypeXyPluginStartDependencies
+ > {
public async setup(
core: VisTypeXyCoreSetup,
{ expressions, visualizations, charts }: VisTypeXyPluginSetupDependencies
) {
- // eslint-disable-next-line no-console
- console.warn(
- 'The visTypeXy plugin is enabled\n\n',
- 'This may negatively alter existing vislib visualization configurations if saved.'
- );
- const visualizationDependencies: Readonly = {
- uiSettings: core.uiSettings,
- charts,
- };
-
- const visTypeDefinitions: any[] = [];
- const visFunctions: any = [];
+ if (core.uiSettings.get(CHARTS_LIBRARY, false)) {
+ setUISettings(core.uiSettings);
+ setThemeService(charts.theme);
+ setColorsService(charts.legacyColors);
- visFunctions.forEach((fn: any) => expressions.registerFunction(fn));
- visTypeDefinitions.forEach((vis: any) =>
- visualizations.createBaseVisualization(vis(visualizationDependencies))
- );
+ [createVisTypeXyVisFn].forEach(expressions.registerFunction);
+ expressions.registerRenderer(xyVisRenderer);
+ visTypesDefinitions.forEach(visualizations.createBaseVisualization);
+ }
return {};
}
- public start(core: CoreStart, deps: VisTypeXyPluginStartDependencies) {
- // nothing to do here
+ public start(core: CoreStart, { data }: VisTypeXyPluginStartDependencies) {
+ setFormatService(data.fieldFormats);
+ setDataActions(data.actions);
+ setTimefilter(data.query.timefilter.timefilter);
+ setDocLinks(core.docLinks);
+
+ return {};
}
}
diff --git a/src/plugins/vis_type_xy/public/services.ts b/src/plugins/vis_type_xy/public/services.ts
new file mode 100644
index 00000000000000..5a72759ecff6cb
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/services.ts
@@ -0,0 +1,49 @@
+/*
+ * 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 { CoreSetup, DocLinksStart } from '../../../core/public';
+import { createGetterSetter } from '../../kibana_utils/public';
+import { DataPublicPluginStart } from '../../data/public';
+import { ChartsPluginSetup } from '../../charts/public';
+
+export const [getUISettings, setUISettings] = createGetterSetter(
+ 'xy core.uiSettings'
+);
+
+export const [getDataActions, setDataActions] = createGetterSetter<
+ DataPublicPluginStart['actions']
+>('xy data.actions');
+
+export const [getFormatService, setFormatService] = createGetterSetter<
+ DataPublicPluginStart['fieldFormats']
+>('xy data.fieldFormats');
+
+export const [getTimefilter, setTimefilter] = createGetterSetter<
+ DataPublicPluginStart['query']['timefilter']['timefilter']
+>('xy data.query.timefilter.timefilter');
+
+export const [getThemeService, setThemeService] = createGetterSetter(
+ 'xy charts.theme'
+);
+
+export const [getColorsService, setColorsService] = createGetterSetter<
+ ChartsPluginSetup['legacyColors']
+>('xy charts.color');
+
+export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks');
diff --git a/src/plugins/vis_type_xy/public/to_ast.test.ts b/src/plugins/vis_type_xy/public/to_ast.test.ts
new file mode 100644
index 00000000000000..678a9faaec5855
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/to_ast.test.ts
@@ -0,0 +1,60 @@
+/*
+ * 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 { Vis } from '../../visualizations/public';
+import { buildExpression } from '../../expressions/public';
+import { sampleAreaVis } from '../../vis_type_vislib/public/sample_vis.test.mocks';
+
+import { toExpressionAst } from './to_ast';
+import { VisParams } from './types';
+
+jest.mock('../../expressions/public', () => ({
+ ...(jest.requireActual('../../expressions/public') as any),
+ buildExpression: jest.fn().mockImplementation(() => ({
+ toAst: () => ({
+ type: 'expression',
+ chain: [],
+ }),
+ })),
+}));
+
+jest.mock('./to_ast_esaggs', () => ({
+ getEsaggsFn: jest.fn(),
+}));
+
+describe('xy vis toExpressionAst function', () => {
+ let vis: Vis;
+
+ const params = {
+ timefilter: {},
+ timeRange: {},
+ abortSignal: {},
+ } as any;
+
+ beforeEach(() => {
+ vis = sampleAreaVis as any;
+ });
+
+ it('should match basic snapshot', () => {
+ toExpressionAst(vis, params);
+ const [, builtExpression] = (buildExpression as jest.Mock).mock.calls[0][0];
+
+ expect(builtExpression).toMatchSnapshot();
+ });
+});
diff --git a/src/plugins/vis_type_xy/public/to_ast.ts b/src/plugins/vis_type_xy/public/to_ast.ts
new file mode 100644
index 00000000000000..c93dbe46dca0e1
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/to_ast.ts
@@ -0,0 +1,94 @@
+/*
+ * 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 from 'moment';
+
+import { VisToExpressionAst, getVisSchemas } from '../../visualizations/public';
+import { buildExpression, buildExpressionFunction } from '../../expressions/public';
+import { BUCKET_TYPES } from '../../data/public';
+
+import { DateHistogramParams, Dimensions, HistogramParams, VisParams } from './types';
+import { visName, VisTypeXyExpressionFunctionDefinition } from './xy_vis_fn';
+import { XyVisType } from '../common';
+import { getEsaggsFn } from './to_ast_esaggs';
+
+export const toExpressionAst: VisToExpressionAst = async (vis, params) => {
+ const schemas = getVisSchemas(vis, params);
+ const dimensions: Dimensions = {
+ x: schemas.segment ? schemas.segment[0] : null,
+ y: schemas.metric,
+ z: schemas.radius,
+ width: schemas.width,
+ series: schemas.group,
+ splitRow: schemas.split_row,
+ splitColumn: schemas.split_column,
+ };
+
+ const responseAggs = vis.data.aggs?.getResponseAggs().filter(({ enabled }) => enabled) ?? [];
+
+ if (dimensions.x) {
+ const xAgg = responseAggs[dimensions.x.accessor] as any;
+ 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;
+ (dimensions.x.params as DateHistogramParams).intervalESValue = esValue;
+ (dimensions.x.params as DateHistogramParams).interval = moment
+ .duration(esValue, esUnit)
+ .asMilliseconds();
+ (dimensions.x.params as DateHistogramParams).format = xAgg.buckets.getScaledDateFormat();
+ } 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, {
+ abortSignal: params.abortSignal,
+ });
+ intervalParam.write(xAgg, output);
+ (dimensions.x.params as HistogramParams).interval = output.params.interval;
+ }
+ }
+
+ const visConfig = { ...vis.params };
+
+ (dimensions.y || []).forEach((yDimension) => {
+ const yAgg = responseAggs[yDimension.accessor];
+ const seriesParam = (visConfig.seriesParams || []).find(
+ (param: any) => param.data.id === yAgg.id
+ );
+ if (seriesParam) {
+ const usedValueAxis = (visConfig.valueAxes || []).find(
+ (valueAxis: any) => valueAxis.id === seriesParam.valueAxis
+ );
+ if (usedValueAxis?.scale.mode === 'percentage') {
+ yDimension.format = { id: 'percent' };
+ }
+ }
+ });
+
+ visConfig.dimensions = dimensions;
+
+ const visTypeXy = buildExpressionFunction(visName, {
+ type: vis.type.name as XyVisType,
+ visConfig: JSON.stringify(visConfig),
+ });
+
+ const ast = buildExpression([getEsaggsFn(vis), visTypeXy]);
+
+ return ast.toAst();
+};
diff --git a/src/plugins/vis_type_xy/public/to_ast_esaggs.ts b/src/plugins/vis_type_xy/public/to_ast_esaggs.ts
new file mode 100644
index 00000000000000..da8d11ac8340c6
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/to_ast_esaggs.ts
@@ -0,0 +1,46 @@
+/*
+ * 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 { Vis } from '../../visualizations/public';
+import { buildExpression, buildExpressionFunction } from '../../expressions/public';
+import {
+ EsaggsExpressionFunctionDefinition,
+ IndexPatternLoadExpressionFunctionDefinition,
+} from '../../data/public';
+
+import { VisParams } 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) {
+ return buildExpressionFunction('esaggs', {
+ index: buildExpression([
+ buildExpressionFunction('indexPatternLoad', {
+ id: vis.data.indexPattern!.id!,
+ }),
+ ]),
+ metricsAtAllLevels: vis.isHierarchical(),
+ partialRows: false,
+ aggs: vis.data.aggs!.aggs.map((agg) => buildExpression(agg.toExpressionAst())),
+ });
+}
diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_type_xy/public/types/config.ts
new file mode 100644
index 00000000000000..ec73c0f6e3fc07
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/types/config.ts
@@ -0,0 +1,130 @@
+/*
+ * 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 {
+ AxisSpec,
+ CustomTooltip,
+ Fit,
+ GridLineStyle,
+ Position,
+ Rotation,
+ SeriesScales,
+ TickFormatter,
+ TooltipProps,
+ TooltipValueFormatter,
+ YDomainRange,
+} from '@elastic/charts';
+
+import { Dimension, Scale, ThresholdLine } from './param';
+
+export interface Column {
+ id: string | null;
+ name: string;
+}
+
+export interface Aspect {
+ accessor: Column['id'];
+ aggType: string | null;
+ aggId: string | null;
+ column?: Dimension['accessor'];
+ title: Column['name'];
+ format?: Dimension['format'];
+ formatter?: TickFormatter;
+ params: Dimension['params'];
+}
+
+export interface Aspects {
+ x: Aspect;
+ y: Aspect[];
+ z?: Aspect;
+ series?: Aspect[];
+}
+
+export interface AxisGrid {
+ show?: boolean;
+ styles?: GridLineStyle;
+}
+
+export interface TickOptions {
+ show?: boolean;
+ size?: number;
+ count?: number;
+ padding?: number;
+ formatter?: TickFormatter;
+ labelFormatter?: TickFormatter;
+ rotation?: number;
+ showDuplicates?: boolean;
+ integersOnly?: boolean;
+ showOverlappingTicks?: boolean;
+ showOverlappingLabels?: boolean;
+}
+
+export type YScaleType = SeriesScales['yScaleType'];
+export type XScaleType = SeriesScales['xScaleType'];
+
+export type ScaleConfig = Omit & {
+ type?: S;
+};
+
+export interface AxisConfig {
+ id: string;
+ groupId?: string;
+ position: Position;
+ ticks?: TickOptions;
+ show: boolean;
+ style: AxisSpec['style'];
+ scale: ScaleConfig;
+ domain?: YDomainRange;
+ title?: string;
+ grid?: AxisGrid;
+ integersOnly: boolean;
+}
+
+export interface LegendOptions {
+ show: boolean;
+ position?: Position;
+}
+
+export type ThresholdLineConfig = Omit & {
+ dash?: number[];
+ groupId?: string;
+};
+
+export type TooltipConfig = Omit & {
+ detailedTooltip?: (headerFormatter?: TooltipValueFormatter) => CustomTooltip;
+};
+
+export interface VisConfig {
+ legend: LegendOptions;
+ tooltip: TooltipConfig;
+ xAxis: AxisConfig;
+ yAxes: Array>;
+ aspects: Aspects;
+ rotation: Rotation;
+ thresholdLine: ThresholdLineConfig;
+ orderBucketsBySum?: boolean;
+ showCurrentTime: boolean;
+ isTimeChart: boolean;
+ markSizeRatio: number;
+ showValueLabel: boolean;
+ enableHistogramMode: boolean;
+ fittingFunction?: Exclude;
+ detailedTooltip?: boolean;
+ isVislibVis?: boolean;
+}
diff --git a/src/plugins/vis_type_xy/public/types/constants.ts b/src/plugins/vis_type_xy/public/types/constants.ts
new file mode 100644
index 00000000000000..f92c56e43413e4
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/types/constants.ts
@@ -0,0 +1,67 @@
+/*
+ * 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';
+
+export const ChartMode = Object.freeze({
+ Normal: 'normal' as const,
+ Stacked: 'stacked' as const,
+});
+export type ChartMode = $Values;
+
+export const InterpolationMode = Object.freeze({
+ Linear: 'linear' as const,
+ Cardinal: 'cardinal' as const,
+ StepAfter: 'step-after' as const,
+});
+export type InterpolationMode = $Values;
+
+export const AxisType = Object.freeze({
+ Category: 'category' as const,
+ Value: 'value' as const,
+});
+export type AxisType = $Values;
+
+export const ScaleType = Object.freeze({
+ Linear: 'linear' as const,
+ Log: 'log' as const,
+ SquareRoot: 'square root' as const,
+});
+export type ScaleType = $Values;
+
+export const AxisMode = Object.freeze({
+ Normal: 'normal' as const,
+ Percentage: 'percentage' as const,
+ Wiggle: 'wiggle' as const,
+ Silhouette: 'silhouette' as const,
+});
+export type AxisMode = $Values;
+
+export const ThresholdLineStyle = Object.freeze({
+ Full: 'full' as const,
+ Dashed: 'dashed' as const,
+ DotDashed: 'dot-dashed' as const,
+});
+export type ThresholdLineStyle = $Values;
+
+export const ColorMode = Object.freeze({
+ Background: 'Background' as const,
+ Labels: 'Labels' as const,
+ None: 'None' as const,
+});
+export type ColorMode = $Values;
diff --git a/src/plugins/vis_type_xy/public/types/index.ts b/src/plugins/vis_type_xy/public/types/index.ts
new file mode 100644
index 00000000000000..791373def20183
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/types/index.ts
@@ -0,0 +1,23 @@
+/*
+ * 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 './constants';
+export * from './config';
+export * from './param';
+export * from './vis_type';
diff --git a/src/plugins/vis_type_xy/public/types/param.ts b/src/plugins/vis_type_xy/public/types/param.ts
new file mode 100644
index 00000000000000..c8cd020dec03ce
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/types/param.ts
@@ -0,0 +1,160 @@
+/*
+ * 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 { Fit, Position } from '@elastic/charts';
+
+import { Style, Labels } from '../../../charts/public';
+import { SchemaConfig } from '../../../visualizations/public';
+
+import { ChartType } from '../../common';
+import {
+ ChartMode,
+ AxisMode,
+ AxisType,
+ InterpolationMode,
+ ScaleType,
+ ThresholdLineStyle,
+} from './constants';
+
+export interface Scale {
+ boundsMargin?: number | '';
+ defaultYExtents?: boolean;
+ max?: number | null;
+ min?: number | null;
+ mode?: AxisMode;
+ setYExtents?: boolean;
+ type: ScaleType;
+}
+
+export interface CategoryAxis {
+ id: string;
+ labels: Labels;
+ position: Position;
+ scale: Scale;
+ show: boolean;
+ title: {
+ text?: string;
+ };
+ type: AxisType;
+ /**
+ * Used only for heatmap, here for consistent types when used in vis_type_vislib
+ *
+ * remove with vis_type_vislib
+ * https://github.com/elastic/kibana/issues/56143
+ */
+ style: Partial