diff --git a/common/constants/explorer.ts b/common/constants/explorer.ts
index 4d07b7033..96908eab0 100644
--- a/common/constants/explorer.ts
+++ b/common/constants/explorer.ts
@@ -82,6 +82,7 @@ export const PLOTLY_GAUGE_COLUMN_NUMBER = 4;
export const APP_ANALYTICS_TAB_ID_REGEX = /application-analytics-tab.+/;
export const DEFAULT_AVAILABILITY_QUERY = 'stats count() by span( timestamp, 1h )';
export const ADD_BUTTON_TEXT = '+ Add color theme';
+export const ADD_SERIES_POSITION_TEXT = '+ Add label position';
export const NUMBER_INPUT_MIN_LIMIT = 1;
export const VIZ_CONTAIN_XY_AXIS = [
@@ -280,3 +281,7 @@ export const DATA_CONFIG_HINTS_INFO = {
[BREAKDOWNS]:
"Defines how each series is broken down. Breakdowns are 'by' clauses that subdivide the existing series.",
};
+export const SERIES_POSITION_OPTIONS = [
+ { id: htmlIdGenerator('ct')(), label: 'Left', side: 'left' },
+ { id: htmlIdGenerator('ct')(), label: 'Right', side: 'right' },
+];
diff --git a/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap
index d8a2d7f31..a61ee008d 100644
--- a/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap
+++ b/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap
@@ -1885,6 +1885,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = `
},
],
},
+ Object {
+ "editor": [Function],
+ "id": "yaxis-side",
+ "mapTo": "seriesPosition",
+ "name": "Series label position",
+ "schemas": Array [],
+ },
Object {
"editor": [Function],
"id": "color-theme",
@@ -2386,6 +2393,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = `
},
],
},
+ Object {
+ "editor": [Function],
+ "id": "yaxis-side",
+ "mapTo": "seriesPosition",
+ "name": "Series label position",
+ "schemas": Array [],
+ },
Object {
"editor": [Function],
"id": "color-theme",
@@ -2901,6 +2915,13 @@ exports[`Utils helper functions renders displayVisualization function 2`] = `
},
],
},
+ Object {
+ "editor": [Function],
+ "id": "yaxis-side",
+ "mapTo": "seriesPosition",
+ "name": "Series label position",
+ "schemas": Array [],
+ },
Object {
"editor": [Function],
"id": "color-theme",
diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap
index 7283927bb..3efa4edcc 100644
--- a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap
+++ b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap
@@ -1410,6 +1410,13 @@ exports[`Config panel component Renders config panel with visualization data 1`]
},
],
},
+ Object {
+ "editor": [Function],
+ "id": "yaxis-side",
+ "mapTo": "seriesPosition",
+ "name": "Series label position",
+ "schemas": Array [],
+ },
Object {
"editor": [Function],
"id": "color-theme",
diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_yaxis_side.tsx b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_yaxis_side.tsx
new file mode 100644
index 000000000..492c4d2e3
--- /dev/null
+++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_yaxis_side.tsx
@@ -0,0 +1,143 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment, useCallback } from 'react';
+import {
+ EuiButton,
+ EuiAccordion,
+ EuiFormRow,
+ EuiSpacer,
+ EuiIcon,
+ EuiFlexGroup,
+ EuiFlexItem,
+ htmlIdGenerator,
+ EuiComboBox,
+} from '@elastic/eui';
+import { isEmpty } from 'lodash';
+import {
+ ADD_SERIES_POSITION_TEXT,
+ AGGREGATIONS,
+ SERIES_POSITION_OPTIONS,
+} from '../../../../../../../../common/constants/explorer';
+import { getPropName } from '../../../../../utils/utils';
+
+export const ConfigYAxisSide = ({
+ visualizations,
+ schemas,
+ vizState = [],
+ handleConfigChange,
+ sectionName = 'Series position',
+}: any) => {
+ const { data } = visualizations;
+ const { metadata: { fields = [] } = {} } = data?.rawVizData;
+ const { dataConfig = {} } = data?.userConfigs;
+
+ const options = (dataConfig[AGGREGATIONS] && dataConfig[AGGREGATIONS].length !== 0
+ ? dataConfig[AGGREGATIONS]
+ : fields
+ ).map((optionItem) => ({
+ ...optionItem,
+ label: getPropName(optionItem),
+ side: 'left',
+ className: 'color-theme-combo-box-option',
+ ctid: htmlIdGenerator('ct')(),
+ }));
+
+ const getUpdatedOptions = () =>
+ options.filter((option) => !vizState.some((vizOpt) => option.label === vizOpt?.label));
+
+ const handleColorThemeChange = useCallback(
+ (ctId, ctName) => (event) => {
+ const value = event.length ? event[0][ctName] : '';
+ handleConfigChange([
+ ...vizState.map((ct) => {
+ if (ctId !== ct.ctid) return ct;
+ return {
+ ...ct,
+ [ctName]: value,
+ };
+ }),
+ ]);
+ },
+ [vizState, handleConfigChange]
+ );
+
+ const handleDeletePosition = useCallback(
+ (ctId) => (event) => handleConfigChange([...vizState.filter((ct) => ct.ctid !== ctId)]),
+ [vizState, handleConfigChange]
+ );
+
+ const handleAddPosition = useCallback(() => {
+ const res = isEmpty(vizState)
+ ? [options.length ? options[0] : { id: htmlIdGenerator('ct')(), label: '', side: 'left' }]
+ : [
+ ...vizState,
+ ...options.filter((option) => !vizState.some((vizOpt) => option.label === vizOpt?.label)),
+ ];
+ handleConfigChange(res);
+ }, [vizState, handleConfigChange]);
+
+ return (
+
+
+ {ADD_SERIES_POSITION_TEXT}
+
+
+ {!isEmpty(vizState) &&
+ vizState.map((ct) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ positionItem.side === ct.side
+ )
+ : []
+ }
+ onChange={handleColorThemeChange(ct.ctid, 'side')}
+ aria-label="series-position-dropdown"
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+ })}
+
+ );
+};
diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx
index 998cf3c71..09a3b59b2 100644
--- a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx
+++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx
@@ -306,11 +306,6 @@ export const DataConfigPanelItem = ({
fillVisDataInStore({ visData: visData, query, visConfMetadata, visMeta });
};
- const isPositionButtonVisible = (sectionName: string) =>
- sectionName === AGGREGATIONS &&
- (visualizations.vis.name === VIS_CHART_TYPES.Line ||
- visualizations.vis.name === VIS_CHART_TYPES.Scatter);
-
const getTimeStampFilteredFields = (options: IField[]) =>
filter(options, (i: IField) => i.type !== TIMESTAMP);
@@ -381,19 +376,6 @@ export const DataConfigPanelItem = ({
)}
{/* Show input fields for dimensions */}
{!isAggregations && getCommonDimensionsField(selectedObj, name)}
- {isPositionButtonVisible(name) && (
-
- updateList(id, 'side')}
- />
-
- )}
diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts
index 10c56703e..c026190a6 100644
--- a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts
+++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/index.ts
@@ -26,3 +26,4 @@ export { ButtonGroupItem } from './config_button_group';
export { TextInputFieldItem } from './config_text_input';
export { ConfigBarChartStyles } from './config_bar_chart_styles';
export { ConfigAvailability } from './config_availability';
+export { ConfigYAxisSide } from './config_yaxis_side';
diff --git a/public/components/visualizations/charts/lines/line.tsx b/public/components/visualizations/charts/lines/line.tsx
index d669c60ed..88de3ae06 100644
--- a/public/components/visualizations/charts/lines/line.tsx
+++ b/public/components/visualizations/charts/lines/line.tsx
@@ -44,6 +44,7 @@ export const Line = ({ visualizations, layout, config }: any) => {
userConfigs: {
dataConfig: {
chartStyles = {},
+ seriesPosition = [],
legend = {},
span = {},
colorTheme = [],
@@ -85,7 +86,8 @@ export const Line = ({ visualizations, layout, config }: any) => {
colorTheme.find((colorSelected) => colorSelected.name.name === field)?.color) ||
PLOTLY_COLOR[index % PLOTLY_COLOR.length];
const timestampField = find(fields, (field) => field.type === 'timestamp');
- let xaxis = [timestampField];
+ const xaxis = [timestampField];
+ let multiYAxisLayout = {};
if (!timestampField || isEmpty(series)) return ;
@@ -94,16 +96,34 @@ export const Line = ({ visualizations, layout, config }: any) => {
return traces.map((trace, idx: number) => {
const selectedColor = getSelectedColorTheme(trace.aggName, idx);
const fillColor = hexToRgb(selectedColor, fillOpacity);
+ const side = seriesPosition.find((seriesItem) => seriesItem.label === trace.name);
+ const multiYaxis = { yaxis: `y${idx + 1}` };
+
+ multiYAxisLayout = {
+ ...multiYAxisLayout,
+ [`yaxis${idx > 0 ? idx + 1 : ''}`]: {
+ titlefont: {
+ color: selectedColor,
+ },
+ automargin: true,
+ tickfont: {
+ color: selectedColor,
+ ...(labelSize && {
+ size: labelSize,
+ }),
+ },
+ ...(idx > 0 && { overlaying: 'y' }),
+ side: side ? side.side : 'left',
+ },
+ };
return {
...trace,
hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
type: 'line',
mode,
- ...{
- fill: 'tozeroy',
- fillcolor: fillColor,
- },
+ fill: 'tozeroy',
+ fillcolor: fillColor,
line: {
shape: lineShape,
width: lineWidth,
@@ -111,14 +131,13 @@ export const Line = ({ visualizations, layout, config }: any) => {
},
marker: {
size: markerSize,
- ...{
- color: fillColor,
- line: {
- color: selectedColor,
- width: lineWidth,
- },
+ color: fillColor,
+ line: {
+ color: selectedColor,
+ width: lineWidth,
},
},
+ ...(idx >= 1 && multiYaxis),
};
});
};
@@ -143,16 +162,7 @@ export const Line = ({ visualizations, layout, config }: any) => {
transformPreprocessedDataToTraces(preprocessJsonData(jsonData, visConfig), visConfig),
traceStyles
);
- }, [
- chartStyles,
- jsonData,
- dimensions,
- series,
- span,
- breakdowns,
- panelOptions,
- tooltipOptions,
- ]);
+ }, [chartStyles, jsonData, dimensions, series, span, breakdowns, panelOptions, tooltipOptions]);
const mergedLayout = useMemo(() => {
const axisLabelsStyle = {
@@ -181,9 +191,7 @@ export const Line = ({ visualizations, layout, config }: any) => {
tickangle: tickAngle,
...axisLabelsStyle,
},
- yaxis: {
- ...axisLabelsStyle,
- },
+ ...multiYAxisLayout,
showlegend: showLegend,
margin: PLOT_MARGIN,
};
diff --git a/public/components/visualizations/charts/lines/line_type.ts b/public/components/visualizations/charts/lines/line_type.ts
index d02558b66..ccbde39ab 100644
--- a/public/components/visualizations/charts/lines/line_type.ts
+++ b/public/components/visualizations/charts/lines/line_type.ts
@@ -14,6 +14,7 @@ import {
ConfigLegend,
InputFieldItem,
ConfigColorTheme,
+ ConfigYAxisSide,
} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability';
import {
@@ -204,6 +205,13 @@ export const createLineTypeDefinition = (params: any = {}) => ({
},
],
},
+ {
+ id: 'yaxis-side',
+ name: 'Series label position',
+ editor: ConfigYAxisSide,
+ mapTo: 'seriesPosition',
+ schemas: [],
+ },
{
id: 'color-theme',
name: 'Color theme',