-
-
handleAddField(field)}
- handleRemoveField={(field: IField) => handleRemoveField(field)}
- isFieldToggleButtonDisabled={true}
+
+
+ {(EuiResizablePanel, EuiResizableButton) => (
+ <>
+
+
+
+ handleAddField(field)}
+ handleRemoveField={(field: IField) => handleRemoveField(field)}
+ isFieldToggleButtonDisabled={
+ vis.name === VIS_CHART_TYPES.LogsView
+ ? isEmpty(explorerData.jsonData) ||
+ !isEmpty(query[RAW_QUERY].match(PPL_STATS_REGEX))
+ : true
+ }
+ />
+
+
{renderDataConfigContainer()}
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
- >
- )}
-
+
+ >
+ )}
+
+
);
};
diff --git a/public/components/event_analytics/explorer/visualizations/shared_components/__tests__/__snapshots__/shared_components.test.tsx.snap b/public/components/event_analytics/explorer/visualizations/shared_components/__tests__/__snapshots__/shared_components.test.tsx.snap
index 8ed6302b0..8511fbe6f 100644
--- a/public/components/event_analytics/explorer/visualizations/shared_components/__tests__/__snapshots__/shared_components.test.tsx.snap
+++ b/public/components/event_analytics/explorer/visualizations/shared_components/__tests__/__snapshots__/shared_components.test.tsx.snap
@@ -30,19 +30,19 @@ exports[`Shared components Renders empty placeholder component 1`] = `
>
diff --git a/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.scss b/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.scss
new file mode 100644
index 000000000..be379ecec
--- /dev/null
+++ b/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.scss
@@ -0,0 +1,12 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+ .lnsChart__empty {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ font-size: 20px;
+}
diff --git a/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.tsx b/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.tsx
index 40d449ea7..54e9f1cb6 100644
--- a/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.tsx
+++ b/public/components/event_analytics/explorer/visualizations/shared_components/empty_placeholder.tsx
@@ -4,19 +4,23 @@
*/
import React from 'react';
-import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui';
+import { EuiIcon, EuiText, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@osd/i18n/react';
+import './empty_placeholder.scss';
-export const EmptyPlaceholder = (props: {icon: string}) => (
+export const EmptyPlaceholder = (props: { icon: string }) => (
<>
-
-
-
+
+
+
-
+
>
diff --git a/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.scss b/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.scss
index 36f5d15c2..56c95de24 100644
--- a/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.scss
+++ b/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.scss
@@ -3,7 +3,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
-
.ws__header-dark {
background-color: transparent;
+}
+
+.ws__visCanvas {
+ grid-gap: 12px;
+ padding: 12px;
+ .ws__visCanvasControl {
+ height: 40px;
+ width: 140px;
+ .ws__visCanvasFlexitem {
+ display: block;
+ }
+ }
+}
+
+#vis__mainContent .ws__central--canvas {
+ background-color: #FFF;
+ border-right: 1px solid #D3DAE6;
}
\ No newline at end of file
diff --git a/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx b/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx
index aaf9a4542..81bcb6e0b 100644
--- a/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx
+++ b/public/components/event_analytics/explorer/visualizations/workspace_panel/workspace_panel.tsx
@@ -7,7 +7,7 @@ import './workspace_panel.scss';
import React, { useState, useMemo } from 'react';
import { isEmpty } from 'lodash';
-import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiSpacer } from '@elastic/eui';
+import { EuiPanel, EuiSwitch } from '@elastic/eui';
import { Visualization } from '../../../../visualizations/visualization';
import { DataTable } from '../../../../visualizations/charts/data_table/data_table';
import { uiSettingsService } from '../../../../../../common/utils';
@@ -21,57 +21,36 @@ interface IWorkSpacePanel {
export function WorkspacePanel({ visualizations }: IWorkSpacePanel) {
const [isTableViewOn, setIsTableViewOn] = useState(false);
const VisualizationPanel = useMemo(() => {
- return ;
+ return (
+
+ );
}, [visualizations]);
return (
- <>
-
+
+ {
+ setIsTableViewOn((staleState) => !staleState);
+ }}
+ aria-describedby="table view switcher"
+ data-test-subj="workspace__dataTableViewSwitch"
+ compressed
+ />
+
+
-
-
-
-
-
- {
- setIsTableViewOn((staleState) => !staleState);
- }}
- aria-describedby="table view switcher"
- data-test-subj="workspace__dataTableViewSwitch"
- compressed
- />
-
-
-
-
-
-
-
- {isTableViewOn ?
- : VisualizationPanel}
-
-
-
- >
+ {isTableViewOn ? : VisualizationPanel}
+
+
);
}
diff --git a/public/components/event_analytics/hooks/index.ts b/public/components/event_analytics/hooks/index.ts
index 68b898f14..0f884788c 100644
--- a/public/components/event_analytics/hooks/index.ts
+++ b/public/components/event_analytics/hooks/index.ts
@@ -6,3 +6,4 @@
export { useFetchEvents } from './use_fetch_events';
export { useFetchVisualizations } from './use_fetch_visualizations';
export { TabContext } from './use_tab_context';
+export { useRenderVisualization } from './use_render_visualizations';
diff --git a/public/components/event_analytics/hooks/use_fetch_visualizations.ts b/public/components/event_analytics/hooks/use_fetch_visualizations.ts
index 93c05425a..1586bb768 100644
--- a/public/components/event_analytics/hooks/use_fetch_visualizations.ts
+++ b/public/components/event_analytics/hooks/use_fetch_visualizations.ts
@@ -38,7 +38,8 @@ export const useFetchVisualizations = ({
const fetchVisualizations = async (
{ query }: { query: string },
format: string,
- handler: (res: any) => void
+ successHandler: (res: any) => void,
+ errorHandler: (error: any) => void
) => {
setIsVisLoading(true);
@@ -46,16 +47,16 @@ export const useFetchVisualizations = ({
.fetch({
query,
format,
+ }, (error) => {
+ errorHandler(error);
+ setIsVisLoading(false);
})
.then((res: any) => {
- handler(res);
- })
- .catch((err: any) => {
- console.error(err);
- })
- .finally(() => {
+ if (res && res.status === 200) {
+ successHandler(res);
+ }
setIsVisLoading(false);
- });
+ })
};
const getCountVisualizations = (interval: string) => {
@@ -75,7 +76,8 @@ export const useFetchVisualizations = ({
data: res,
})
);
- }
+ },
+ (error: Error) => {}
);
};
@@ -124,6 +126,14 @@ export const useFetchVisualizations = ({
})
);
});
+ },
+ (error: any) => {
+ dispatch(
+ renderExplorerVis({
+ tabId: requestParams.tabId,
+ data: error.body,
+ })
+ );
}
);
};
diff --git a/public/components/event_analytics/hooks/use_render_visualizations.ts b/public/components/event_analytics/hooks/use_render_visualizations.ts
new file mode 100644
index 000000000..2ebbc9996
--- /dev/null
+++ b/public/components/event_analytics/hooks/use_render_visualizations.ts
@@ -0,0 +1,134 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { batch, useDispatch } from 'react-redux';
+import { updateFields, sortFields } from '../redux/slices/field_slice';
+import { render as renderExplorerVis } from '../redux/slices/visualization_slice';
+import { fetchSuccess } from '../redux/slices/query_result_slice';
+import {
+ QUERIED_FIELDS,
+ SELECTED_FIELDS,
+} from '../../../../common/constants/explorer';
+import { change as changeVizConfig } from '../redux/slices/viualization_config_slice';
+import { changeQuery } from '../redux/slices/query_slice';
+import { VisualizationState } from 'common/types/explorer';
+
+export interface IVisualizationParams {
+ query: string;
+ callback: (res: any) => void | null;
+}
+
+export const useRenderVisualization = ({ pplService, requestParams }) => {
+ const dispatch = useDispatch();
+ const fetchVisualizations = async (
+ { query }: { query: string },
+ format: string,
+ successHandler: (res: any) => void,
+ errorHandler: (error: any) => void
+ ) => {
+ await pplService
+ .fetch(
+ {
+ query,
+ format,
+ },
+ (error) => {
+ errorHandler(error);
+ }
+ )
+ .then((res: any) => {
+ if (res && res.status === 200) {
+ successHandler(res);
+ }
+ });
+ };
+
+ const getVisualizations = ({ query, callback }: IVisualizationParams) => {
+ fetchVisualizations(
+ {
+ query,
+ },
+ 'viz',
+ (res: any) => {
+ callback && callback(res);
+ },
+ (error: any) => {
+ dispatch(
+ renderExplorerVis({
+ tabId: requestParams.tabId,
+ data: error.body,
+ })
+ );
+ }
+ );
+ };
+
+ const fillVisDataInStore = ({ visData, query, visConfMetadata, visMeta }: VisualizationState) => {
+ batch(() => {
+ // query
+ dispatch(
+ changeQuery({
+ tabId: requestParams.tabId,
+ query,
+ })
+ );
+
+ // explorerVisualization
+ dispatch(
+ renderExplorerVis({
+ tabId: requestParams.tabId,
+ data: visData,
+ })
+ );
+
+ // queryResults
+ dispatch(
+ fetchSuccess({
+ tabId: requestParams.tabId,
+ data: {
+ jsonData: visData?.jsonData || {},
+ },
+ })
+ );
+
+ // fields
+ dispatch(
+ updateFields({
+ tabId: requestParams.tabId,
+ data: {
+ [QUERIED_FIELDS]: visData?.metadata?.fields || [],
+ [SELECTED_FIELDS]: [],
+ },
+ })
+ );
+
+ // sort fields
+ dispatch(
+ sortFields({
+ tabId: requestParams.tabId,
+ data: [QUERIED_FIELDS],
+ })
+ );
+
+ // explorerVisualizationConfig
+ dispatch(
+ changeVizConfig({
+ tabId: requestParams.tabId,
+ vizId: visMeta.visId,
+ data: {
+ dataConfig: {
+ ...visConfMetadata,
+ },
+ },
+ })
+ );
+ });
+ };
+
+ return {
+ getVisualizations,
+ fillVisDataInStore,
+ };
+};
diff --git a/public/components/event_analytics/index.tsx b/public/components/event_analytics/index.tsx
index edac5e494..ff4c22513 100644
--- a/public/components/event_analytics/index.tsx
+++ b/public/components/event_analytics/index.tsx
@@ -5,7 +5,7 @@
import { EuiGlobalToastList } from '@elastic/eui';
import { Toast } from '@elastic/eui/src/components/toast/global_toast_list';
-import { EmptyTabParams } from 'common/types/explorer';
+import { EmptyTabParams, EventAnalyticsProps } from 'common/types/explorer';
import { isEmpty } from 'lodash';
import React, { ReactChild, useState } from 'react';
import { HashRouter, Route, Switch, useHistory } from 'react-router-dom';
@@ -23,8 +23,9 @@ export const EventAnalytics = ({
timestampUtils,
http,
notifications,
+ queryManager,
...props
-}: any) => {
+}: EventAnalyticsProps) => {
const history = useHistory();
const [toasts, setToasts] = useState([]);
@@ -84,6 +85,7 @@ export const EventAnalytics = ({
getExistingEmptyTab={getExistingEmptyTab}
history={history}
notifications={notifications}
+ queryManager={queryManager}
/>
);
}}
diff --git a/public/components/event_analytics/utils/__tests__/utils.test.tsx b/public/components/event_analytics/utils/__tests__/utils.test.tsx
new file mode 100644
index 000000000..12d9a9cda
--- /dev/null
+++ b/public/components/event_analytics/utils/__tests__/utils.test.tsx
@@ -0,0 +1,83 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+import {
+ hexToRgb,
+ lightenColor,
+ formatError,
+ isValidTraceId,
+ rangeNumDocs,
+ getHeaders,
+} from '../utils';
+
+describe('Utils event analytics helper functions', () => {
+ configure({ adapter: new Adapter() });
+
+ it('validates hexToRgb function', () => {
+ expect(hexToRgb()).toBe('rgba(60,161,199,1)');
+ expect(hexToRgb('test', 1, true)).toBe('rgba(96,353,409,1)');
+ expect(hexToRgb('#000000', 1, true)).toBe('rgba(0,0,0,1)');
+ });
+
+ it('validates lightenColor function', () => {
+ expect(lightenColor('#00000', 10)).toBe('#1a1a1a');
+ });
+
+ it('validates formatError function', () => {
+ expect(formatError('Warning', 'This is a warning', 'Test warning description')).toStrictEqual({
+ body: {
+ attributes: { error: { caused_by: { reason: 'Test warning description', type: '' } } },
+ },
+ message: 'This is a warning',
+ name: 'Warning',
+ });
+ });
+
+ it('validates isValidTraceId function', () => {
+ expect(isValidTraceId('#00000')).toBe(false);
+ expect(isValidTraceId('abcdefghijklmnopqrstuvwxyzabcdef')).toBe(true);
+ });
+
+ it('validates rangeNumDocs function', () => {
+ expect(rangeNumDocs(11000)).toBe(10000);
+ expect(rangeNumDocs(-200)).toBe(0);
+ expect(rangeNumDocs(2000)).toBe(2000);
+ });
+
+ it('validates getHeaders function', () => {
+ expect(
+ getHeaders(
+ [
+ {
+ name: 'host',
+ type: 'text',
+ },
+ {
+ name: 'ip_count',
+ type: 'integer',
+ },
+ {
+ name: 'per_ip_bytes',
+ type: 'long',
+ },
+ {
+ name: 'resp_code',
+ type: 'text',
+ },
+ {
+ name: 'sum_bytes',
+ type: 'long',
+ },
+ ],
+ ['', 'Time', '_source'],
+ undefined
+ )
+ ).toBeTruthy();
+ expect(getHeaders([], ['', 'Time', '_source'], undefined)).toBeTruthy();
+ });
+});
diff --git a/public/components/event_analytics/utils/utils.tsx b/public/components/event_analytics/utils/utils.tsx
index d142f472f..889a53b7a 100644
--- a/public/components/event_analytics/utils/utils.tsx
+++ b/public/components/event_analytics/utils/utils.tsx
@@ -1,18 +1,25 @@
+/* eslint-disable no-bitwise */
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
+import dateMath from '@elastic/datemath';
import { uniqueId } from 'lodash';
-import React from 'react';
import moment from 'moment';
-import dateMath from '@elastic/datemath';
-import { IExplorerFields, IField } from '../../../../common/types/explorer';
-import { DocViewRow, IDocType } from '../explorer/events_views';
+import React from 'react';
import { HttpStart } from '../../../../../../src/core/public';
-import PPLService from '../../../services/requests/ppl';
-import { TIME_INTERVAL_OPTIONS } from '../../../../common/constants/explorer';
+import { CUSTOM_LABEL, TIME_INTERVAL_OPTIONS } from '../../../../common/constants/explorer';
import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/shared';
+import {
+ ConfigListEntry,
+ GetTooltipHoverInfoType,
+ IExplorerFields,
+ IField,
+} from '../../../../common/types/explorer';
+import PPLService from '../../../services/requests/ppl';
+import { DocViewRow, IDocType } from '../explorer/events_views';
+import { ConfigTooltip } from '../explorer/visualizations/config_panel/config_panes/config_controls';
// Create Individual table rows for events datagrid and flyouts
export const getTrs = (
@@ -285,3 +292,101 @@ export const findAutoInterval = (start: string = '', end: string = '') => {
return [minInterval, [{ text: 'Auto', value: 'auto_' + minInterval }, ...TIME_INTERVAL_OPTIONS]];
};
+
+export const hexToRgb = (
+ hex: string = '#3CA1C7',
+ opacity: number = 1,
+ colorWithOpacity: boolean = true
+) => {
+ // default color PLOTLY_COLOR[0]: '#3CA1C7'
+ const defaultColor = [hex, '60', '161', '199'];
+ const rgbElements = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) || defaultColor;
+ const [, r, g, b] = rgbElements.map((color) => parseInt(color, 16));
+ const rgbaFormat = colorWithOpacity ? `rgba(${r},${g},${b},${opacity})` : `rgb(${r},${g},${b})`;
+ return rgbaFormat;
+};
+
+export const lightenColor = (color: string, percent: number) => {
+ const num = parseInt(color.replace('#', ''), 16);
+ const amt = Math.round(2.55 * percent);
+ const R = (num >> 16) + amt;
+ const B = ((num >> 8) & 0x00ff) + amt;
+ const G = (num & 0x0000ff) + amt;
+ return (
+ '#' +
+ (
+ 0x1000000 +
+ (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
+ (B < 255 ? (B < 1 ? 0 : B) : 255) * 0x100 +
+ (G < 255 ? (G < 1 ? 0 : G) : 255)
+ )
+ .toString(16)
+ .slice(1)
+ );
+};
+
+// Get config objects according to specific editor
+export const fetchConfigObject = (editor: string, propsOptions: any) => {
+ switch (editor) {
+ case 'Tooltip':
+ return {
+ id: 'tooltip_options',
+ name: 'Tooltip options',
+ editor: ConfigTooltip,
+ mapTo: 'tooltipOptions',
+ schemas: [
+ {
+ name: 'Tooltip mode',
+ component: null,
+ mapTo: 'tooltipMode',
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: 'show' }],
+ },
+ },
+ {
+ name: 'Tooltip text',
+ component: null,
+ mapTo: 'tooltipText',
+ props: propsOptions,
+ },
+ ],
+ };
+ default:
+ return null;
+ }
+};
+
+export const getTooltipHoverInfo = ({ tooltipMode, tooltipText }: GetTooltipHoverInfoType) => {
+ if (tooltipMode === 'hidden') {
+ return 'none';
+ }
+ if (tooltipText === undefined) {
+ return 'all';
+ }
+ return tooltipText;
+};
+
+export const filterDataConfigParameter = (parameter: ConfigListEntry[]) =>
+ parameter.filter((configItem: ConfigListEntry) => configItem.label);
+
+export const getRoundOf = (value: number, places: number) => value.toFixed(places);
+
+export const getPropName = (queriedVizObj: {
+ customLabel?: string;
+ aggregation: string;
+ name: string;
+ label: string;
+}) => {
+ if (queriedVizObj) {
+ if (queriedVizObj[CUSTOM_LABEL] === '' || queriedVizObj[CUSTOM_LABEL] === undefined) {
+ return `${queriedVizObj.aggregation}(${queriedVizObj.name})`;
+ }
+ return queriedVizObj[CUSTOM_LABEL];
+ } else {
+ return '';
+ }
+};
diff --git a/public/components/index.tsx b/public/components/index.tsx
index 267cb6b5d..eec21a18b 100644
--- a/public/components/index.tsx
+++ b/public/components/index.tsx
@@ -5,6 +5,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import { QueryManager } from 'common/query_manager';
import { AppMountParameters, CoreStart } from '../../../../src/core/public';
import { AppPluginStartDependencies } from '../types';
import { App } from './app';
@@ -16,7 +17,8 @@ export const Observability = (
pplService: any,
dslService: any,
savedObjects: any,
- timestampUtils: any
+ timestampUtils: any,
+ queryManager: QueryManager
) => {
ReactDOM.render(
,
AppMountParametersProp.element
);
diff --git a/public/components/trace_analytics/components/common/plots/box_plt.tsx b/public/components/trace_analytics/components/common/plots/box_plt.tsx
index d3c04f1cb..b177fc2c5 100644
--- a/public/components/trace_analytics/components/common/plots/box_plt.tsx
+++ b/public/components/trace_analytics/components/common/plots/box_plt.tsx
@@ -4,6 +4,7 @@
*/
import { EuiFlexGrid, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui';
+import { BarOrientation } from '../../../../../../common/constants/shared';
import React, { useMemo, useState } from 'react';
import { Plt } from '../../../../visualizations/plotly/plot';
@@ -53,7 +54,7 @@ export function BoxPlt({ plotParams }: { plotParams: PlotParamsType }) {
x: [plotParams.left],
y: [0],
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
width: 1,
marker: { color: 'rgba(0, 0, 0, 0)' },
hoverinfo: 'none',
@@ -63,7 +64,7 @@ export function BoxPlt({ plotParams }: { plotParams: PlotParamsType }) {
x: [plotParams.mid - plotParams.left],
y: [0],
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
width: 1,
marker: {
color: plotParams.currPercentileFilter === '< 95th' ? '#fcfcfc' : '#ffffff',
@@ -82,7 +83,7 @@ export function BoxPlt({ plotParams }: { plotParams: PlotParamsType }) {
x: [plotParams.right - plotParams.mid],
y: [0],
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
width: 1,
marker: {
color: plotParams.currPercentileFilter === '>= 95th' ? '#aea4d1' : '#957ac9',
diff --git a/public/components/trace_analytics/components/common/plots/service_map_scale.tsx b/public/components/trace_analytics/components/common/plots/service_map_scale.tsx
index 86d11b7ec..e9c900bcc 100644
--- a/public/components/trace_analytics/components/common/plots/service_map_scale.tsx
+++ b/public/components/trace_analytics/components/common/plots/service_map_scale.tsx
@@ -4,6 +4,7 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui';
+import { BarOrientation } from '../../../../../../common/constants/shared';
import _ from 'lodash';
import React, { useEffect, useState } from 'react';
import { Plt } from '../../../../visualizations/plotly/plot';
@@ -56,7 +57,7 @@ export function ServiceMapScale(props: {
{
x: Array.from({ length: result.data.y.length }, () => 0),
type: 'bar',
- orientation: 'v',
+ orientation: BarOrientation.vertical,
width: 0.4,
hoverinfo: 'none',
showlegend: false,
diff --git a/public/components/trace_analytics/components/traces/__tests__/span_detail_panel.test.tsx b/public/components/trace_analytics/components/traces/__tests__/span_detail_panel.test.tsx
index bcad5bd54..c652224bb 100644
--- a/public/components/trace_analytics/components/traces/__tests__/span_detail_panel.test.tsx
+++ b/public/components/trace_analytics/components/traces/__tests__/span_detail_panel.test.tsx
@@ -8,6 +8,7 @@ import { render } from '@testing-library/react';
import { configure, mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { SpanDetailPanel } from '../span_detail_panel';
+import { BarOrientation } from '../../../../../../common/constants/shared';
describe('Service breakdown panel component', () => {
configure({ adapter: new Adapter() });
@@ -21,7 +22,7 @@ describe('Service breakdown panel component', () => {
marker: { color: 'rgba(0, 0, 0, 0)' },
width: 0.4,
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
hoverinfo: 'none',
showlegend: false,
},
@@ -34,7 +35,7 @@ describe('Service breakdown panel component', () => {
marker: { color: '#7492e7' },
width: 0.4,
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
hovertemplate: '%{x}',
},
] as Plotly.Data[],
diff --git a/public/components/trace_analytics/requests/traces_request_handler.ts b/public/components/trace_analytics/requests/traces_request_handler.ts
index 03eb33f4d..876680865 100644
--- a/public/components/trace_analytics/requests/traces_request_handler.ts
+++ b/public/components/trace_analytics/requests/traces_request_handler.ts
@@ -4,6 +4,7 @@
*/
/* eslint-disable no-console */
+import { BarOrientation } from '../../../../common/constants/shared';
import _ from 'lodash';
import moment from 'moment';
import { v1 as uuid } from 'uuid';
@@ -242,7 +243,7 @@ const hitsToSpanDetailData = async (hits: any, colorMap: any) => {
},
width: 0.4,
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
hoverinfo: 'none',
showlegend: false,
spanId: hit._source.spanId,
@@ -258,7 +259,7 @@ const hitsToSpanDetailData = async (hits: any, colorMap: any) => {
},
width: 0.4,
type: 'bar',
- orientation: 'h',
+ orientation: BarOrientation.horizontal,
hovertemplate: '%{x}',
spanId: hit._source.spanId,
}
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap
index 9a1ab2672..a1688524f 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Bar component Renders bar component 1`] = `
+exports[`Veritcal Bar component Renders veritcal bar component 1`] = `
@@ -406,75 +582,44 @@ exports[`Bar component Renders bar component 1`] = `
"displayModeBar": false,
}
}
- data={
- Array [
- Object {
- "marker": Object {
- "color": Array [
- "#3CA1C7",
- "#8C55A3",
- "#DB748A",
- "#F2BE4B",
- "#68CCC2",
- "#2A7866",
- ],
- },
- "name": "count()",
- "orientation": "v",
- "type": "bar",
- "x": Array [
- "error",
- "info",
- "login",
- "security",
- "success",
- "warning",
- ],
- "y": Array [
- 154,
- 1753,
- 116,
- 468,
- 1964,
- 219,
- ],
- },
- ]
- }
+ data={Array []}
debug={false}
divId="explorerPlotComponent"
layout={
Object {
"autosize": true,
- "barmode": "",
+ "bargap": 0.30000000000000004,
+ "bargroupgap": 0.030000000000000027,
+ "barmode": "group",
"colorway": Array [
"#8C55A3",
],
"height": 220,
"hovermode": "closest",
"legend": Object {
- "orientation": "h",
- "traceorder": "normal",
+ "orientation": "v",
},
"margin": Object {
- "b": 15,
- "l": 60,
- "pad": 0,
- "r": 10,
- "t": 30,
+ "b": 30,
+ "l": 30,
+ "pad": 4,
+ "r": 5,
+ "t": 50,
},
- "showlegend": true,
+ "showlegend": "show",
"title": "",
"xaxis": Object {
"automargin": true,
- "rangemode": "normal",
- "showgrid": true,
- "zeroline": false,
+ "tickangle": 0,
+ "tickfont": Object {
+ "size": 12,
+ },
},
"yaxis": Object {
- "rangemode": "normal",
- "showgrid": true,
- "zeroline": false,
+ "automargin": true,
+ "tickfont": Object {
+ "size": 12,
+ },
},
}
}
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap
index f6f8243c1..98c317106 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/data_table.test.tsx.snap
@@ -1,504 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Data table component Renders data table component 1`] = `
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ rowHeight={35}
+ suppressFieldDotNotation={true}
+ suppressPaginationPanel={true}
+ />
+
`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap
new file mode 100644
index 000000000..742cbbc53
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap
@@ -0,0 +1,465 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Gauge component Renders gauge component 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No results found
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap
index 56fc05d26..dbf0a9173 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap
@@ -199,104 +199,288 @@ exports[`Heatmap component Renders heatmap component 1`] = `
"status": 200,
},
},
- "userConfigs": Object {},
+ "userConfigs": Object {
+ "dataConfig": Object {
+ "valueOptions": Object {
+ "dimensions": Array [
+ Object {
+ "label": "tags",
+ "name": "tags",
+ "type": "text",
+ },
+ ],
+ "metrics": Array [
+ Object {
+ "label": "count()",
+ "name": "count()",
+ "side": "left",
+ "type": "integer",
+ },
+ ],
+ },
+ },
+ },
},
"vis": Object {
+ "barwidth": 0.97,
"category": "Visualizations",
- "categoryAxis": "xaxis",
+ "categoryaxis": "xaxis",
"component": [Function],
- "editorConfig": Object {
+ "editorconfig": Object {
"panelTabs": Array [
Object {
"editor": [Function],
"id": "data-panel",
"mapTo": "dataConfig",
- "name": "Data",
+ "name": "Style",
"sections": Array [
Object {
"editor": [Function],
- "id": "value_options",
- "mapTo": "valueOptions",
- "name": "Value options",
+ "id": "tooltip_options",
+ "mapTo": "tooltipOptions",
+ "name": "Tooltip options",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "xaxis",
- "name": "X-axis",
+ "mapTo": "tooltipMode",
+ "name": "Tooltip mode",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ Object {
+ "id": "hidden",
+ "name": "Hidden",
+ },
+ ],
+ },
},
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "yaxis",
- "name": "Y-axis",
+ "mapTo": "tooltipText",
+ "name": "Tooltip text",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ Object {
+ "id": "x",
+ "name": "Dimension",
+ },
+ Object {
+ "id": "y",
+ "name": "Series",
+ },
+ ],
+ },
},
],
},
Object {
"editor": [Function],
- "id": "chart_options",
- "mapTo": "chartOptions",
- "name": "Chart options",
+ "id": "legend",
+ "mapTo": "legend",
+ "name": "Legend",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": true,
- "mapTo": "orientation",
- "name": "Orientation",
+ "mapTo": "showLegend",
+ "name": "Show legend",
"props": Object {
"defaultSelections": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
Object {
- "name": "Horizontal",
- "orientationId": "h",
+ "id": "hidden",
+ "name": "Hidden",
},
],
},
},
Object {
"component": null,
- "isSingleSelection": true,
+ "mapTo": "position",
+ "name": "Position",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ Object {
+ "id": "h",
+ "name": "Bottom",
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "legendSize",
+ "name": "Legend size",
+ "title": "Legend size",
+ },
+ ],
+ },
+ Object {
+ "editor": [Function],
+ "id": "chart_styles",
+ "mapTo": "chartStyles",
+ "name": "Chart styles",
+ "schemas": Array [
+ Object {
+ "component": [Function],
+ "eleType": "buttons",
"mapTo": "mode",
"name": "Mode",
"props": Object {
"defaultSelections": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
Object {
- "modeId": "stack",
+ "id": "stack",
"name": "Stack",
},
],
},
},
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "labelSize",
+ "name": "Label size",
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0,
+ "eleType": "slider",
+ "mapTo": "rotateBarLabels",
+ "name": "Rotate bar labels",
+ "props": Object {
+ "max": 90,
+ "min": -90,
+ "showTicks": true,
+ "ticks": Array [
+ Object {
+ "label": "-90°",
+ "value": -90,
+ },
+ Object {
+ "label": "-45°",
+ "value": -45,
+ },
+ Object {
+ "label": "0°",
+ "value": 0,
+ },
+ Object {
+ "label": "45°",
+ "value": 45,
+ },
+ Object {
+ "label": "90°",
+ "value": 90,
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.7,
+ "eleType": "slider",
+ "mapTo": "groupWidth",
+ "name": "Group width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.97,
+ "eleType": "slider",
+ "mapTo": "barWidth",
+ "name": "Bar width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 2,
+ "eleType": "slider",
+ "mapTo": "lineWidth",
+ "name": "Line width",
+ "props": Object {
+ "max": 10,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 70,
+ "eleType": "slider",
+ "mapTo": "fillOpacity",
+ "name": "Fill opacity",
+ "props": Object {
+ "max": 100,
+ },
+ },
],
},
+ Object {
+ "editor": [Function],
+ "id": "color-theme",
+ "mapTo": "colorTheme",
+ "name": "Color theme",
+ "schemas": Array [],
+ },
],
},
Object {
- "content": Array [],
"editor": [Function],
- "id": "style-panel",
- "mapTo": "layoutConfig",
- "name": "Layout",
+ "id": "availability-panel",
+ "mapTo": "availabilityConfig",
+ "name": "Availability",
},
Object {
"editor": [Function],
@@ -306,26 +490,33 @@ exports[`Heatmap component Renders heatmap component 1`] = `
},
],
},
- "fullLabel": "Bar",
+ "fillopacity": 70,
+ "fulllabel": "Vertical bar",
+ "groupwidth": 0.7,
"icon": [Function],
- "iconType": "visBarVerticalStacked",
+ "icontype": "visBarVerticalStacked",
"id": "bar",
- "label": "Bar",
+ "label": "Vertical bar",
+ "labelangle": 0,
+ "legendposition": "v",
+ "linewidth": 2,
+ "mode": "group",
"name": "bar",
"orientation": "v",
"selection": Object {
"dataLoss": "nothing",
},
- "seriesAxis": "yaxis",
+ "seriesaxis": "yaxis",
+ "showlegend": "show",
"type": "bar",
- "visConfig": Object {
+ "visconfig": Object {
"config": Object {
"displaylogo": false,
"responsive": true,
},
"isUniColor": false,
"layout": Object {
- "height": 500,
+ "height": 1180,
"legend": Object {
"orientation": "v",
"traceorder": "normal",
@@ -373,19 +564,19 @@ exports[`Heatmap component Renders heatmap component 1`] = `
>
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap
new file mode 100644
index 000000000..ac914b72e
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap
@@ -0,0 +1,674 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Histogram component Renders histogram component 1`] = `
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap
new file mode 100644
index 000000000..6856a232f
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap
@@ -0,0 +1,646 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Horizontal bar component Renders horizontal bar component 1`] = `
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap
index d767d0c93..1dea3d499 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap
@@ -199,104 +199,288 @@ exports[`Line component Renders line component 1`] = `
"status": 200,
},
},
- "userConfigs": Object {},
+ "userConfigs": Object {
+ "dataConfig": Object {
+ "valueOptions": Object {
+ "dimensions": Array [
+ Object {
+ "label": "tags",
+ "name": "tags",
+ "type": "text",
+ },
+ ],
+ "metrics": Array [
+ Object {
+ "label": "count()",
+ "name": "count()",
+ "side": "left",
+ "type": "integer",
+ },
+ ],
+ },
+ },
+ },
},
"vis": Object {
+ "barwidth": 0.97,
"category": "Visualizations",
- "categoryAxis": "xaxis",
+ "categoryaxis": "xaxis",
"component": [Function],
- "editorConfig": Object {
+ "editorconfig": Object {
"panelTabs": Array [
Object {
"editor": [Function],
"id": "data-panel",
"mapTo": "dataConfig",
- "name": "Data",
+ "name": "Style",
"sections": Array [
Object {
"editor": [Function],
- "id": "value_options",
- "mapTo": "valueOptions",
- "name": "Value options",
+ "id": "tooltip_options",
+ "mapTo": "tooltipOptions",
+ "name": "Tooltip options",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "xaxis",
- "name": "X-axis",
+ "mapTo": "tooltipMode",
+ "name": "Tooltip mode",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ Object {
+ "id": "hidden",
+ "name": "Hidden",
+ },
+ ],
+ },
},
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "yaxis",
- "name": "Y-axis",
+ "mapTo": "tooltipText",
+ "name": "Tooltip text",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ Object {
+ "id": "x",
+ "name": "Dimension",
+ },
+ Object {
+ "id": "y",
+ "name": "Series",
+ },
+ ],
+ },
},
],
},
Object {
"editor": [Function],
- "id": "chart_options",
- "mapTo": "chartOptions",
- "name": "Chart options",
+ "id": "legend",
+ "mapTo": "legend",
+ "name": "Legend",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": true,
- "mapTo": "orientation",
- "name": "Orientation",
+ "mapTo": "showLegend",
+ "name": "Show legend",
"props": Object {
"defaultSelections": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
Object {
- "name": "Horizontal",
- "orientationId": "h",
+ "id": "hidden",
+ "name": "Hidden",
},
],
},
},
Object {
"component": null,
- "isSingleSelection": true,
+ "mapTo": "position",
+ "name": "Position",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ Object {
+ "id": "h",
+ "name": "Bottom",
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "legendSize",
+ "name": "Legend size",
+ "title": "Legend size",
+ },
+ ],
+ },
+ Object {
+ "editor": [Function],
+ "id": "chart_styles",
+ "mapTo": "chartStyles",
+ "name": "Chart styles",
+ "schemas": Array [
+ Object {
+ "component": [Function],
+ "eleType": "buttons",
"mapTo": "mode",
"name": "Mode",
"props": Object {
"defaultSelections": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
Object {
- "modeId": "stack",
+ "id": "stack",
"name": "Stack",
},
],
},
},
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "labelSize",
+ "name": "Label size",
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0,
+ "eleType": "slider",
+ "mapTo": "rotateBarLabels",
+ "name": "Rotate bar labels",
+ "props": Object {
+ "max": 90,
+ "min": -90,
+ "showTicks": true,
+ "ticks": Array [
+ Object {
+ "label": "-90°",
+ "value": -90,
+ },
+ Object {
+ "label": "-45°",
+ "value": -45,
+ },
+ Object {
+ "label": "0°",
+ "value": 0,
+ },
+ Object {
+ "label": "45°",
+ "value": 45,
+ },
+ Object {
+ "label": "90°",
+ "value": 90,
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.7,
+ "eleType": "slider",
+ "mapTo": "groupWidth",
+ "name": "Group width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.97,
+ "eleType": "slider",
+ "mapTo": "barWidth",
+ "name": "Bar width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 2,
+ "eleType": "slider",
+ "mapTo": "lineWidth",
+ "name": "Line width",
+ "props": Object {
+ "max": 10,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 70,
+ "eleType": "slider",
+ "mapTo": "fillOpacity",
+ "name": "Fill opacity",
+ "props": Object {
+ "max": 100,
+ },
+ },
],
},
+ Object {
+ "editor": [Function],
+ "id": "color-theme",
+ "mapTo": "colorTheme",
+ "name": "Color theme",
+ "schemas": Array [],
+ },
],
},
Object {
- "content": Array [],
"editor": [Function],
- "id": "style-panel",
- "mapTo": "layoutConfig",
- "name": "Layout",
+ "id": "availability-panel",
+ "mapTo": "availabilityConfig",
+ "name": "Availability",
},
Object {
"editor": [Function],
@@ -306,26 +490,33 @@ exports[`Line component Renders line component 1`] = `
},
],
},
- "fullLabel": "Bar",
+ "fillopacity": 70,
+ "fulllabel": "Vertical bar",
+ "groupwidth": 0.7,
"icon": [Function],
- "iconType": "visBarVerticalStacked",
+ "icontype": "visBarVerticalStacked",
"id": "bar",
- "label": "Bar",
+ "label": "Vertical bar",
+ "labelangle": 0,
+ "legendposition": "v",
+ "linewidth": 2,
+ "mode": "group",
"name": "bar",
"orientation": "v",
"selection": Object {
"dataLoss": "nothing",
},
- "seriesAxis": "yaxis",
+ "seriesaxis": "yaxis",
+ "showlegend": "show",
"type": "bar",
- "visConfig": Object {
+ "visconfig": Object {
"config": Object {
"displaylogo": false,
"responsive": true,
},
"isUniColor": false,
"layout": Object {
- "height": 500,
+ "height": 1180,
"legend": Object {
"orientation": "v",
"traceorder": "normal",
@@ -344,137 +535,82 @@ exports[`Line component Renders line component 1`] = `
}
}
>
-
-
-
-
+ className="euiText euiText--extraSmall lnsChart__empty"
+ data-test-subj="vizWorkspace__noData"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No results found
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap
new file mode 100644
index 000000000..64ebbd437
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap
@@ -0,0 +1,588 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Logs View component Renders logs view component 1`] = `
+
+
+
+
+
+
+
+ |
+
+ Time
+ |
+
+ _source
+ |
+
+
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/metrics.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/metrics.test.tsx.snap
new file mode 100644
index 000000000..3b51e9fab
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/metrics.test.tsx.snap
@@ -0,0 +1,595 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Metrics component Renders Metrics component 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No results found
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap
index fffaeaa8e..8f71c11bf 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap
@@ -199,104 +199,288 @@ exports[`Pie component Renders pie component 1`] = `
"status": 200,
},
},
- "userConfigs": Object {},
+ "userConfigs": Object {
+ "dataConfig": Object {
+ "valueOptions": Object {
+ "dimensions": Array [
+ Object {
+ "label": "tags",
+ "name": "tags",
+ "type": "text",
+ },
+ ],
+ "metrics": Array [
+ Object {
+ "label": "count()",
+ "name": "count()",
+ "side": "left",
+ "type": "integer",
+ },
+ ],
+ },
+ },
+ },
},
"vis": Object {
+ "barwidth": 0.97,
"category": "Visualizations",
- "categoryAxis": "xaxis",
+ "categoryaxis": "xaxis",
"component": [Function],
- "editorConfig": Object {
+ "editorconfig": Object {
"panelTabs": Array [
Object {
"editor": [Function],
"id": "data-panel",
"mapTo": "dataConfig",
- "name": "Data",
+ "name": "Style",
"sections": Array [
Object {
"editor": [Function],
- "id": "value_options",
- "mapTo": "valueOptions",
- "name": "Value options",
+ "id": "tooltip_options",
+ "mapTo": "tooltipOptions",
+ "name": "Tooltip options",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "xaxis",
- "name": "X-axis",
+ "mapTo": "tooltipMode",
+ "name": "Tooltip mode",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ Object {
+ "id": "hidden",
+ "name": "Hidden",
+ },
+ ],
+ },
},
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "yaxis",
- "name": "Y-axis",
+ "mapTo": "tooltipText",
+ "name": "Tooltip text",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ Object {
+ "id": "x",
+ "name": "Dimension",
+ },
+ Object {
+ "id": "y",
+ "name": "Series",
+ },
+ ],
+ },
},
],
},
Object {
"editor": [Function],
- "id": "chart_options",
- "mapTo": "chartOptions",
- "name": "Chart options",
+ "id": "legend",
+ "mapTo": "legend",
+ "name": "Legend",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": true,
- "mapTo": "orientation",
- "name": "Orientation",
+ "mapTo": "showLegend",
+ "name": "Show legend",
"props": Object {
"defaultSelections": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
Object {
- "name": "Horizontal",
- "orientationId": "h",
+ "id": "hidden",
+ "name": "Hidden",
},
],
},
},
Object {
"component": null,
- "isSingleSelection": true,
+ "mapTo": "position",
+ "name": "Position",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ Object {
+ "id": "h",
+ "name": "Bottom",
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "legendSize",
+ "name": "Legend size",
+ "title": "Legend size",
+ },
+ ],
+ },
+ Object {
+ "editor": [Function],
+ "id": "chart_styles",
+ "mapTo": "chartStyles",
+ "name": "Chart styles",
+ "schemas": Array [
+ Object {
+ "component": [Function],
+ "eleType": "buttons",
"mapTo": "mode",
"name": "Mode",
"props": Object {
"defaultSelections": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
Object {
- "modeId": "stack",
+ "id": "stack",
"name": "Stack",
},
],
},
},
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "labelSize",
+ "name": "Label size",
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0,
+ "eleType": "slider",
+ "mapTo": "rotateBarLabels",
+ "name": "Rotate bar labels",
+ "props": Object {
+ "max": 90,
+ "min": -90,
+ "showTicks": true,
+ "ticks": Array [
+ Object {
+ "label": "-90°",
+ "value": -90,
+ },
+ Object {
+ "label": "-45°",
+ "value": -45,
+ },
+ Object {
+ "label": "0°",
+ "value": 0,
+ },
+ Object {
+ "label": "45°",
+ "value": 45,
+ },
+ Object {
+ "label": "90°",
+ "value": 90,
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.7,
+ "eleType": "slider",
+ "mapTo": "groupWidth",
+ "name": "Group width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.97,
+ "eleType": "slider",
+ "mapTo": "barWidth",
+ "name": "Bar width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 2,
+ "eleType": "slider",
+ "mapTo": "lineWidth",
+ "name": "Line width",
+ "props": Object {
+ "max": 10,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 70,
+ "eleType": "slider",
+ "mapTo": "fillOpacity",
+ "name": "Fill opacity",
+ "props": Object {
+ "max": 100,
+ },
+ },
],
},
+ Object {
+ "editor": [Function],
+ "id": "color-theme",
+ "mapTo": "colorTheme",
+ "name": "Color theme",
+ "schemas": Array [],
+ },
],
},
Object {
- "content": Array [],
"editor": [Function],
- "id": "style-panel",
- "mapTo": "layoutConfig",
- "name": "Layout",
+ "id": "availability-panel",
+ "mapTo": "availabilityConfig",
+ "name": "Availability",
},
Object {
"editor": [Function],
@@ -306,26 +490,33 @@ exports[`Pie component Renders pie component 1`] = `
},
],
},
- "fullLabel": "Bar",
+ "fillopacity": 70,
+ "fulllabel": "Vertical bar",
+ "groupwidth": 0.7,
"icon": [Function],
- "iconType": "visBarVerticalStacked",
+ "icontype": "visBarVerticalStacked",
"id": "bar",
- "label": "Bar",
+ "label": "Vertical bar",
+ "labelangle": 0,
+ "legendposition": "v",
+ "linewidth": 2,
+ "mode": "group",
"name": "bar",
"orientation": "v",
"selection": Object {
"dataLoss": "nothing",
},
- "seriesAxis": "yaxis",
+ "seriesaxis": "yaxis",
+ "showlegend": "show",
"type": "bar",
- "visConfig": Object {
+ "visconfig": Object {
"config": Object {
"displaylogo": false,
"responsive": true,
},
"isUniColor": false,
"layout": Object {
- "height": 500,
+ "height": 1180,
"legend": Object {
"orientation": "v",
"traceorder": "normal",
@@ -344,147 +535,82 @@ exports[`Pie component Renders pie component 1`] = `
}
}
>
-
-
-
-
+ className="euiText euiText--extraSmall lnsChart__empty"
+ data-test-subj="vizWorkspace__noData"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No results found
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap
index d52e5c2d9..06516fd7d 100644
--- a/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap
@@ -182,104 +182,288 @@ exports[`Text component Renders text component 1`] = `
"status": 200,
},
},
- "userConfigs": Object {},
+ "userConfigs": Object {
+ "dataConfig": Object {
+ "valueOptions": Object {
+ "dimensions": Array [
+ Object {
+ "label": "tags",
+ "name": "tags",
+ "type": "text",
+ },
+ ],
+ "metrics": Array [
+ Object {
+ "label": "count()",
+ "name": "count()",
+ "side": "left",
+ "type": "integer",
+ },
+ ],
+ },
+ },
+ },
},
"vis": Object {
+ "barwidth": 0.97,
"category": "Visualizations",
- "categoryAxis": "xaxis",
+ "categoryaxis": "xaxis",
"component": [Function],
- "editorConfig": Object {
+ "editorconfig": Object {
"panelTabs": Array [
Object {
"editor": [Function],
"id": "data-panel",
"mapTo": "dataConfig",
- "name": "Data",
+ "name": "Style",
"sections": Array [
Object {
"editor": [Function],
- "id": "value_options",
- "mapTo": "valueOptions",
- "name": "Value options",
+ "id": "tooltip_options",
+ "mapTo": "tooltipOptions",
+ "name": "Tooltip options",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "xaxis",
- "name": "X-axis",
+ "mapTo": "tooltipMode",
+ "name": "Tooltip mode",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "show",
+ "name": "Show",
+ },
+ Object {
+ "id": "hidden",
+ "name": "Hidden",
+ },
+ ],
+ },
},
Object {
"component": null,
- "isSingleSelection": false,
- "mapTo": "yaxis",
- "name": "Y-axis",
+ "mapTo": "tooltipText",
+ "name": "Tooltip text",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "all",
+ "name": "All",
+ },
+ Object {
+ "id": "x",
+ "name": "Dimension",
+ },
+ Object {
+ "id": "y",
+ "name": "Series",
+ },
+ ],
+ },
},
],
},
Object {
"editor": [Function],
- "id": "chart_options",
- "mapTo": "chartOptions",
- "name": "Chart options",
+ "id": "legend",
+ "mapTo": "legend",
+ "name": "Legend",
"schemas": Array [
Object {
"component": null,
- "isSingleSelection": true,
- "mapTo": "orientation",
- "name": "Orientation",
+ "mapTo": "showLegend",
+ "name": "Show legend",
"props": Object {
"defaultSelections": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "name": "Vertical",
- "orientationId": "v",
+ "id": "show",
+ "name": "Show",
},
Object {
- "name": "Horizontal",
- "orientationId": "h",
+ "id": "hidden",
+ "name": "Hidden",
},
],
},
},
Object {
"component": null,
- "isSingleSelection": true,
+ "mapTo": "position",
+ "name": "Position",
+ "props": Object {
+ "defaultSelections": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ ],
+ "options": Array [
+ Object {
+ "id": "v",
+ "name": "Right",
+ },
+ Object {
+ "id": "h",
+ "name": "Bottom",
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "legendSize",
+ "name": "Legend size",
+ "title": "Legend size",
+ },
+ ],
+ },
+ Object {
+ "editor": [Function],
+ "id": "chart_styles",
+ "mapTo": "chartStyles",
+ "name": "Chart styles",
+ "schemas": Array [
+ Object {
+ "component": [Function],
+ "eleType": "buttons",
"mapTo": "mode",
"name": "Mode",
"props": Object {
"defaultSelections": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
],
- "dropdownList": Array [
+ "options": Array [
Object {
- "modeId": "group",
+ "id": "group",
"name": "Group",
},
Object {
- "modeId": "stack",
+ "id": "stack",
"name": "Stack",
},
],
},
},
+ Object {
+ "component": [Function],
+ "eleType": "input",
+ "mapTo": "labelSize",
+ "name": "Label size",
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0,
+ "eleType": "slider",
+ "mapTo": "rotateBarLabels",
+ "name": "Rotate bar labels",
+ "props": Object {
+ "max": 90,
+ "min": -90,
+ "showTicks": true,
+ "ticks": Array [
+ Object {
+ "label": "-90°",
+ "value": -90,
+ },
+ Object {
+ "label": "-45°",
+ "value": -45,
+ },
+ Object {
+ "label": "0°",
+ "value": 0,
+ },
+ Object {
+ "label": "45°",
+ "value": 45,
+ },
+ Object {
+ "label": "90°",
+ "value": 90,
+ },
+ ],
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.7,
+ "eleType": "slider",
+ "mapTo": "groupWidth",
+ "name": "Group width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 0.97,
+ "eleType": "slider",
+ "mapTo": "barWidth",
+ "name": "Bar width",
+ "props": Object {
+ "max": 1,
+ "step": 0.01,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 2,
+ "eleType": "slider",
+ "mapTo": "lineWidth",
+ "name": "Line width",
+ "props": Object {
+ "max": 10,
+ },
+ },
+ Object {
+ "component": [Function],
+ "defaultState": 70,
+ "eleType": "slider",
+ "mapTo": "fillOpacity",
+ "name": "Fill opacity",
+ "props": Object {
+ "max": 100,
+ },
+ },
],
},
+ Object {
+ "editor": [Function],
+ "id": "color-theme",
+ "mapTo": "colorTheme",
+ "name": "Color theme",
+ "schemas": Array [],
+ },
],
},
Object {
- "content": Array [],
"editor": [Function],
- "id": "style-panel",
- "mapTo": "layoutConfig",
- "name": "Layout",
+ "id": "availability-panel",
+ "mapTo": "availabilityConfig",
+ "name": "Availability",
},
Object {
"editor": [Function],
@@ -289,26 +473,33 @@ exports[`Text component Renders text component 1`] = `
},
],
},
- "fullLabel": "Bar",
+ "fillopacity": 70,
+ "fulllabel": "Vertical bar",
+ "groupwidth": 0.7,
"icon": [Function],
- "iconType": "visBarVerticalStacked",
+ "icontype": "visBarVerticalStacked",
"id": "bar",
- "label": "Bar",
+ "label": "Vertical bar",
+ "labelangle": 0,
+ "legendposition": "v",
+ "linewidth": 2,
+ "mode": "group",
"name": "bar",
"orientation": "v",
"selection": Object {
"dataLoss": "nothing",
},
- "seriesAxis": "yaxis",
+ "seriesaxis": "yaxis",
+ "showlegend": "show",
"type": "bar",
- "visConfig": Object {
+ "visconfig": Object {
"config": Object {
"displaylogo": false,
"responsive": true,
},
"isUniColor": false,
"layout": Object {
- "height": 500,
+ "height": 1180,
"legend": Object {
"orientation": "v",
"traceorder": "normal",
diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap
new file mode 100644
index 000000000..052616ff4
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap
@@ -0,0 +1,699 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Treemap component Renders treemap component 1`] = `
+
+
+
+
+
+
+
+`;
diff --git a/public/components/visualizations/charts/__tests__/bar.test.tsx b/public/components/visualizations/charts/__tests__/bar.test.tsx
index 421f5f27c..8da6bc875 100644
--- a/public/components/visualizations/charts/__tests__/bar.test.tsx
+++ b/public/components/visualizations/charts/__tests__/bar.test.tsx
@@ -13,10 +13,10 @@ import {
TEST_VISUALIZATIONS_DATA
} from '../../../../../test/event_analytics_constants';
-describe('Bar component', () => {
+describe('Veritcal Bar component', () => {
configure({ adapter: new Adapter() });
- it('Renders bar component', async () => {
+ it('Renders veritcal bar component', async () => {
const wrapper = mount(
{
configure({ adapter: new Adapter() });
it('Renders data table component', async () => {
- const wrapper = mount(
-
- );
-
- wrapper.update();
-
+ const gridWrapper = shallow();
+ const agGridReactObj = gridWrapper.find(AgGridReact);
+ agGridReactObj.simulate('gridReady');
+ expect(agGridReactObj).toBeTruthy();
await waitFor(() => {
- expect(wrapper).toMatchSnapshot();
+ expect(gridWrapper).toMatchSnapshot();
});
});
});
diff --git a/public/components/visualizations/charts/__tests__/gauge.test.tsx b/public/components/visualizations/charts/__tests__/gauge.test.tsx
new file mode 100644
index 000000000..6b539445f
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/gauge.test.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { Gauge } from '../financial/gauge/gauge';
+import {
+ LAYOUT_CONFIG,
+ GAUGE_TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Gauge component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders gauge component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/__tests__/histogram.test.tsx b/public/components/visualizations/charts/__tests__/histogram.test.tsx
new file mode 100644
index 000000000..6eb1ebfff
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/histogram.test.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { Histogram } from '../histogram/histogram';
+import {
+ LAYOUT_CONFIG,
+ TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Histogram component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders histogram component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/__tests__/horizontal_bar.test.tsx b/public/components/visualizations/charts/__tests__/horizontal_bar.test.tsx
new file mode 100644
index 000000000..8bc87bab3
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/horizontal_bar.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { Bar } from '../bar/bar';
+import {
+ LAYOUT_CONFIG,
+ HORIZONTAL_BAR_TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Horizontal bar component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders horizontal bar component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/__tests__/logs_view.test.tsx b/public/components/visualizations/charts/__tests__/logs_view.test.tsx
new file mode 100644
index 000000000..e467d6055
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/logs_view.test.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { LogsView } from '../logs_view/logs_view';
+import {
+ LAYOUT_CONFIG,
+ TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Logs View component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders logs view component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/__tests__/metrics.test.tsx b/public/components/visualizations/charts/__tests__/metrics.test.tsx
new file mode 100644
index 000000000..4e12fd2db
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/metrics.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { Metrics } from '../metrics/metrics';
+import {
+ LAYOUT_CONFIG,
+ METRICS_TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Metrics component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders Metrics component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/__tests__/treemap.test.tsx b/public/components/visualizations/charts/__tests__/treemap.test.tsx
new file mode 100644
index 000000000..5210bd416
--- /dev/null
+++ b/public/components/visualizations/charts/__tests__/treemap.test.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import React from 'react';
+import { waitFor } from '@testing-library/react';
+import { TreeMap } from '../maps/treemaps';
+import {
+ LAYOUT_CONFIG,
+ TEST_VISUALIZATIONS_DATA,
+} from '../../../../../test/event_analytics_constants';
+
+describe('Treemap component', () => {
+ configure({ adapter: new Adapter() });
+
+ it('Renders treemap component', async () => {
+ const wrapper = mount(
+
+ );
+
+ wrapper.update();
+
+ await waitFor(() => {
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/components/visualizations/charts/bar/bar.tsx b/public/components/visualizations/charts/bar/bar.tsx
index 83ec1e393..86ca4769b 100644
--- a/public/components/visualizations/charts/bar/bar.tsx
+++ b/public/components/visualizations/charts/bar/bar.tsx
@@ -3,121 +3,221 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
-import { isEmpty, last, take } from 'lodash';
-import { Plt } from '../../plotly/plot';
-import { LONG_CHART_COLOR, PLOTLY_COLOR } from '../../../../../common/constants/shared';
+import React, { useMemo } from 'react';
+import { isEmpty, last } from 'lodash';
+import {
+ AGGREGATIONS,
+ BREAKDOWNS,
+ DEFAULT_BAR_CHART_STYLES,
+ GROUPBY,
+} from '../../../../../common/constants/explorer';
+import {
+ BarOrientation,
+ FILLOPACITY_DIV_FACTOR,
+ LONG_CHART_COLOR,
+ PLOTLY_COLOR,
+ THRESHOLD_LINE_OPACITY,
+ THRESHOLD_LINE_WIDTH,
+ PLOT_MARGIN,
+} from '../../../../../common/constants/shared';
+import { IVisualizationContainerProps } from '../../../../../common/types/explorer';
import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability';
import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds';
+import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { hexToRgb } from '../../../event_analytics/utils/utils';
+import { Plt } from '../../plotly/plot';
+import { transformPreprocessedDataToTraces, preprocessJsonData } from '../shared/common';
export const Bar = ({ visualizations, layout, config }: any) => {
- const { vis } = visualizations;
const {
- data,
- metadata: { fields },
- } = visualizations.data.rawVizData;
- const { isUniColor } = vis.visConfig;
+ data: {
+ rawVizData: {
+ data: queriedVizData,
+ jsonData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ colorTheme = [],
+ chartStyles = {},
+ span = {},
+ legend = {},
+ panelOptions = {},
+ tooltipOptions = {},
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ [BREAKDOWNS]: breakdowns = [],
+ } = {},
+ layoutConfig = {},
+ availabilityConfig = {},
+ } = {},
+ },
+ vis: {
+ type,
+ icontype,
+ fillopacity,
+ orientation,
+ labelangle,
+ linewidth,
+ barwidth,
+ groupwidth,
+ showlegend,
+ legendposition,
+ },
+ }: IVisualizationContainerProps = visualizations;
+
const lastIndex = fields.length - 1;
- const {
- dataConfig = {},
- layoutConfig = {},
- availabilityConfig = {},
- } = visualizations?.data?.userConfigs;
- const xaxis =
- dataConfig.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.xaxis : [];
- const yaxis =
- dataConfig.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig?.valueOptions.yaxis : [];
- const barOrientation =
- dataConfig?.chartOptions?.orientation &&
- dataConfig.chartOptions.orientation[0] &&
- dataConfig.chartOptions.orientation[0].orientationId
- ? dataConfig.chartOptions.orientation[0].orientationId
- : visualizations.vis.orientation;
- const { defaultAxes } = visualizations.data;
- const isVertical = barOrientation === 'v';
+ /**
+ * determine stylings
+ */
+ const barOrientation = chartStyles.orientation || orientation;
+ const isVertical = barOrientation === BarOrientation.vertical;
+ const tickAngle = chartStyles.rotateBarLabels || labelangle;
+ const lineWidth = chartStyles.lineWidth || linewidth;
+ const fillOpacity =
+ chartStyles.fillOpacity !== undefined
+ ? chartStyles.fillOpacity / FILLOPACITY_DIV_FACTOR
+ : fillopacity / FILLOPACITY_DIV_FACTOR;
+ const tooltipMode =
+ tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show';
+ const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all';
+ const barWidth = 1 - (chartStyles.barWidth || barwidth);
+ const groupWidth = 1 - (chartStyles.groupWidth || groupwidth);
+ const showLegend = legend.showLegend ?? showlegend;
+ const legendPosition = legend.position || legendposition;
+ const labelSize = chartStyles.labelSize || DEFAULT_BAR_CHART_STYLES.LabelSize;
+ const legendSize = legend.legendSize;
+ const getSelectedColorTheme = (field: any, index: number) =>
+ (colorTheme.length > 0 &&
+ colorTheme.find((colorSelected) => colorSelected.name.label === field)?.color) ||
+ PLOTLY_COLOR[index % PLOTLY_COLOR.length];
+ // If chart has length of result buckets < 16
+ // then use the LONG_CHART_COLOR for all the bars in the chart
+ const plotlyColorway =
+ queriedVizData[fields[lastIndex].name].length < 16 ? PLOTLY_COLOR : [LONG_CHART_COLOR];
- // Individual bars have different colors
- // when: stackLength = 1 and length of result buckets < 16 and chart is not unicolor
- // Else each stacked bar has its own color using colorway
- let marker = {};
- if (lastIndex === 1 && data[fields[lastIndex].name].length < 16 && !isUniColor) {
- marker = {
- color: data[fields[lastIndex].name].map((_: string, index: number) => {
- return PLOTLY_COLOR[index % PLOTLY_COLOR.length];
- }),
- };
- }
+ if (
+ isEmpty(queriedVizData) ||
+ !Array.isArray(dimensions) ||
+ !Array.isArray(series) ||
+ (breakdowns && !Array.isArray(breakdowns))
+ )
+ return ;
- let valueSeries;
- if (!isEmpty(xaxis) && !isEmpty(yaxis)) {
- valueSeries = isVertical ? [...yaxis] : [...xaxis];
- } else {
- valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1);
- }
+ const addStylesToTraces = (traces, traceStyles) => {
+ const { barOrientation, fillOpacity, tooltipMode, tooltipText, lineWidth } = traceStyles;
+ return traces.map((trace, idx: number) => {
+ const selectedColor = getSelectedColorTheme(trace.aggName, idx);
+ return {
+ ...trace,
+ type,
+ orientation: barOrientation,
+ hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
+ ...{
+ marker: {
+ color: hexToRgb(selectedColor, fillOpacity),
+ line: {
+ color: selectedColor,
+ width: lineWidth,
+ },
+ },
+ },
+ };
+ });
+ };
- // determine category axis
- let bars = valueSeries.map((field: any) => {
- return {
- x: isVertical
- ? data[!isEmpty(xaxis) ? xaxis[0].label : fields[lastIndex].name]
- : data[field.name],
- y: isVertical
- ? data[field.name]
- : data[!isEmpty(yaxis) ? yaxis[0]?.label : fields[lastIndex].name],
- type: vis.type,
- marker,
- name: field.name,
- orientation: barOrientation,
+ let bars = useMemo(() => {
+ const visConfig = {
+ dimensions,
+ series,
+ breakdowns,
+ span,
+ isVertical,
+ };
+ const traceStyles = {
+ barOrientation,
+ fillOpacity,
+ tooltipMode,
+ tooltipText,
+ lineWidth,
};
- });
- // If chart has length of result buckets < 16
- // then use the LONG_CHART_COLOR for all the bars in the chart
- const plotlyColorway =
- data[fields[lastIndex].name].length < 16 ? PLOTLY_COLOR : [LONG_CHART_COLOR];
+ return addStylesToTraces(
+ transformPreprocessedDataToTraces(preprocessJsonData(jsonData, visConfig), visConfig),
+ { ...traceStyles }
+ );
+ }, [chartStyles, jsonData, dimensions, series, breakdowns, span, tooltipOptions]);
- const mergedLayout = {
- colorway: plotlyColorway,
- ...layout,
- ...(layoutConfig.layout && layoutConfig.layout),
- title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '',
- barmode:
- dataConfig?.chartOptions?.mode &&
- dataConfig.chartOptions.mode[0] &&
- dataConfig.chartOptions.mode[0].modeId
- ? dataConfig.chartOptions.mode[0].modeId
- : '',
- };
+ const mergedLayout = useMemo(() => {
+ return {
+ colorway: plotlyColorway,
+ ...layout,
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ barmode: chartStyles.mode || visualizations.vis.mode,
+ xaxis: {
+ ...(isVertical && { tickangle: tickAngle }),
+ automargin: true,
+ tickfont: {
+ ...(labelSize && {
+ size: labelSize,
+ }),
+ },
+ },
+ yaxis: {
+ ...(!isVertical && { tickangle: tickAngle }),
+ automargin: true,
+ tickfont: {
+ ...(labelSize && {
+ size: labelSize,
+ }),
+ },
+ },
+ bargap: groupWidth,
+ bargroupgap: barWidth,
+ legend: {
+ ...layout.legend,
+ orientation: legendPosition,
+ ...(legendSize && {
+ font: {
+ size: legendSize,
+ },
+ }),
+ },
+ showlegend,
+ hovermode: 'closest',
+ margin: PLOT_MARGIN,
+ };
+ }, [visualizations, layout, panelOptions, showLegend, chartStyles]);
- if (dataConfig.thresholds || availabilityConfig.level) {
+ if (availabilityConfig.level) {
const thresholdTraces = {
x: [],
y: [],
mode: 'text',
text: [],
};
- const thresholds = dataConfig.thresholds ? dataConfig.thresholds : [];
const levels = availabilityConfig.level ? availabilityConfig.level : [];
-
const mapToLine = (list: ThresholdUnitType[] | AvailabilityUnitType[], lineStyle: any) => {
return list.map((thr: ThresholdUnitType) => {
thresholdTraces.x.push(
- data[!isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name][0]
+ queriedVizData[
+ !isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name
+ ][0]
);
thresholdTraces.y.push(thr.value * (1 + 0.06));
thresholdTraces.text.push(thr.name);
return {
type: 'line',
- x0: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0],
+ x0: queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0],
y0: thr.value,
- x1: last(data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]),
+ x1: last(queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]),
y1: thr.value,
name: thr.name || '',
- opacity: 0.7,
+ opacity: THRESHOLD_LINE_OPACITY,
line: {
color: thr.color,
- width: 3,
+ width: THRESHOLD_LINE_WIDTH,
...lineStyle,
},
};
diff --git a/public/components/visualizations/charts/bar/bar_type.ts b/public/components/visualizations/charts/bar/bar_type.ts
index fb89427bc..bbb41ac33 100644
--- a/public/components/visualizations/charts/bar/bar_type.ts
+++ b/public/components/visualizations/charts/bar/bar_type.ts
@@ -8,98 +8,209 @@ import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_conf
import { LensIconChartBar } from '../../assets/chart_bar';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } 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 {
+ ConfigLegend,
+ InputFieldItem,
+ ConfigColorTheme,
+ SliderConfig,
+ ConfigBarChartStyles,
+ ButtonGroupItem,
+ ConfigAvailability,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { BarOrientation, DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
+import { VIS_CHART_TYPES } from '../../../../../common/constants/shared';
+import { DEFAULT_BAR_CHART_STYLES } from '../../../../../common/constants/explorer';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const { LegendPosition, ShowLegend, LabelAngle, FillOpacity, LineWidth } = DEFAULT_CHART_STYLES;
+const { BarMode, GroupWidth, BarWidth } = DEFAULT_BAR_CHART_STYLES;
+const isHorizontalBar = (paramstype: string) =>
+ paramstype === VIS_CHART_TYPES.HorizontalBar ? true : false;
+
export const createBarTypeDefinition = (params: any) => ({
- name: 'bar',
+ name: params.type || 'bar',
type: 'bar',
- id: 'bar',
- label: 'Bar',
- fullLabel: 'Bar',
- iconType: 'visBarVerticalStacked',
+ id: params.type || 'bar',
+ label: isHorizontalBar(params.type) ? 'Horizontal bar' : 'Vertical bar',
+ fulllabel: isHorizontalBar(params.type) ? 'Horizontal bar' : 'Vertical bar',
+ icontype: isHorizontalBar(params.type) ? 'visBarHorizontalStacked' : 'visBarVerticalStacked',
selection: {
dataLoss: 'nothing',
},
category: VIS_CATEGORY.BASICS,
icon: LensIconChartBar,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
- orientation: 'v',
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ orientation: isHorizontalBar(params.type) ? BarOrientation.horizontal : BarOrientation.vertical,
+ mode: BarMode,
+ labelangle: LabelAngle,
+ linewidth: LineWidth,
+ fillopacity: FillOpacity,
+ groupwidth: GroupWidth,
+ barwidth: BarWidth,
+ showlegend: ShowLegend,
+ legendposition: LegendPosition,
component: Bar,
- editorConfig: {
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Dimension', id: 'x' },
+ { name: 'Series', id: 'y' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
schemas: [
{
- name: 'X-axis',
- isSingleSelection: false,
+ name: 'Show legend',
+ mapTo: 'showLegend',
component: null,
- mapTo: 'xaxis',
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: ShowLegend }],
+ },
},
{
- name: 'Y-axis',
- isSingleSelection: false,
+ name: 'Position',
+ mapTo: 'position',
component: null,
- mapTo: 'yaxis',
+ props: {
+ options: [
+ { name: 'Right', id: 'v' },
+ { name: 'Bottom', id: 'h' },
+ ],
+ defaultSelections: [{ name: 'Right', id: LegendPosition }],
+ },
+ },
+ {
+ title: 'Legend size',
+ name: 'Legend size',
+ component: InputFieldItem,
+ mapTo: 'legendSize',
+ eleType: 'input',
},
],
},
{
- id: 'chart_options',
- name: 'Chart options',
- editor: ConfigValueOptions,
- mapTo: 'chartOptions',
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigBarChartStyles,
+ mapTo: 'chartStyles',
schemas: [
{
- name: 'Orientation',
- isSingleSelection: true,
- component: null,
- mapTo: 'orientation',
+ name: 'Mode',
+ component: ButtonGroupItem,
+ mapTo: 'mode',
+ eleType: 'buttons',
props: {
- dropdownList: [
- { name: 'Vertical', orientationId: 'v' },
- { name: 'Horizontal', orientationId: 'h' },
+ options: [
+ { name: 'Group', id: BarMode },
+ { name: 'Stack', id: 'stack' },
],
- defaultSelections: [{ name: 'Vertical', orientationId: 'v' }],
+ defaultSelections: [{ name: 'Group', id: BarMode }],
},
},
{
- name: 'Mode',
- isSingleSelection: true,
- component: null,
- mapTo: 'mode',
+ name: 'Label size',
+ component: InputFieldItem,
+ mapTo: 'labelSize',
+ eleType: 'input',
+ },
+ {
+ name: 'Rotate bar labels',
+ component: SliderConfig,
+ mapTo: 'rotateBarLabels',
+ eleType: 'slider',
+ defaultState: LabelAngle,
props: {
- dropdownList: [
- { name: 'Group', modeId: 'group' },
- { name: 'Stack', modeId: 'stack' },
+ ticks: [
+ { label: '-90°', value: -90 },
+ { label: '-45°', value: -45 },
+ { label: '0°', value: 0 },
+ { label: '45°', value: 45 },
+ { label: '90°', value: 90 },
],
- defaultSelections: [{ name: 'Group', modeId: 'group' }],
+ showTicks: true,
+ min: -90,
+ max: 90,
+ },
+ },
+ {
+ name: 'Group width',
+ component: SliderConfig,
+ mapTo: 'groupWidth',
+ defaultState: GroupWidth,
+ props: {
+ max: 1,
+ step: 0.01,
+ },
+ eleType: 'slider',
+ },
+ {
+ name: 'Bar width',
+ component: SliderConfig,
+ mapTo: 'barWidth',
+ defaultState: BarWidth,
+ props: {
+ max: 1,
+ step: 0.01,
+ },
+ eleType: 'slider',
+ },
+ {
+ name: 'Line width',
+ component: SliderConfig,
+ mapTo: 'lineWidth',
+ defaultState: LineWidth,
+ props: {
+ max: 10,
},
+ eleType: 'slider',
+ },
+ {
+ name: 'Fill opacity',
+ component: SliderConfig,
+ mapTo: 'fillOpacity',
+ defaultState: FillOpacity,
+ props: {
+ max: 100,
+ },
+ eleType: 'slider',
},
],
},
+ {
+ id: 'color-theme',
+ name: 'Color theme',
+ editor: ConfigColorTheme,
+ mapTo: 'colorTheme',
+ schemas: [],
+ },
],
},
{
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
+ id: 'availability-panel',
+ name: 'Availability',
+ mapTo: 'availabilityConfig',
+ editor: ConfigAvailability,
},
{
id: 'availability-panel',
@@ -109,7 +220,7 @@ export const createBarTypeDefinition = (params: any) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
},
diff --git a/public/components/visualizations/charts/bar/horizontal_bar_type.ts b/public/components/visualizations/charts/bar/horizontal_bar_type.ts
deleted file mode 100644
index cbc25a1dc..000000000
--- a/public/components/visualizations/charts/bar/horizontal_bar_type.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import { Bar } from './bar';
-import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
-import { LensIconChartBar } from '../../assets/chart_bar';
-import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
-import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
-
-const sharedConfigs = getPlotlySharedConfigs();
-const VIS_CATEGORY = getPlotlyCategory();
-
-export interface BarTypeParams {}
-
-export const createHorizontalBarTypeDefinition = (params: BarTypeParams = {}) => ({
- id: 'horizontal_bar',
- name: 'horizontal_bar',
- type: 'bar',
- label: 'Bar',
- fullLabel: 'Bar',
- iconType: 'visBarVerticalStacked',
- selection: {
- dataLoss: 'nothing',
- },
- category: VIS_CATEGORY.BASICS,
- icon: LensIconChartBar,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
- orientation: 'h',
- component: Bar,
- editorConfig: {
- panelTabs: [
- {
- id: 'data-panel',
- name: 'Data',
- mapTo: 'dataConfig',
- editor: VizDataPanel,
- sections: [
- {
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
- schemas: [
- {
- name: 'X-axis',
- isSingleSelection: false,
- component: null,
- mapTo: 'xaxis',
- },
- {
- name: 'Y-axis',
- isSingleSelection: false,
- component: null,
- mapTo: 'yaxis',
- },
- ],
- },
- {
- id: 'chart_options',
- name: 'Chart options',
- editor: ConfigValueOptions,
- mapTo: 'chartOptions',
- schemas: [
- {
- name: 'Orientation',
- isSingleSelection: true,
- component: null,
- mapTo: 'orientation',
- props: {
- dropdownList: [
- { name: 'Vertical', orientationId: 'v' },
- { name: 'Horizontal', orientationId: 'h' },
- ],
- defaultSelections: [{ name: 'Horizontal', orientationId: 'h' }],
- },
- },
- {
- name: 'Mode',
- isSingleSelection: true,
- component: null,
- mapTo: 'mode',
- props: {
- dropdownList: [
- { name: 'Group', modeId: 'group' },
- { name: 'Stack', modeId: 'stack' },
- ],
- defaultSelections: [{ name: 'Group', modeId: 'group' }],
- },
- },
- ],
- },
- ],
- },
- {
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
- },
- ],
- },
- visConfig: {
- layout: {
- ...sharedConfigs.layout,
- },
- config: {
- ...sharedConfigs.config,
- },
- isUniColor: false,
- },
-});
diff --git a/public/components/visualizations/charts/bubble/bubble_type.ts b/public/components/visualizations/charts/bubble/bubble_type.ts
index 091298ce5..b4cac8ded 100644
--- a/public/components/visualizations/charts/bubble/bubble_type.ts
+++ b/public/components/visualizations/charts/bubble/bubble_type.ts
@@ -18,16 +18,16 @@ export const createBubbleVisDefinition = () => ({
type: 'bubble',
id: 'bubble',
label: 'Bubble',
- fullLabel: 'Bubble',
+ fulllabel: 'Bubble',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
- editorConfig: {
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
@@ -63,7 +63,7 @@ export const createBubbleVisDefinition = () => ({
],
},
icon: LensIconChartPie,
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
},
diff --git a/public/components/visualizations/charts/data_table/data_table.scss b/public/components/visualizations/charts/data_table/data_table.scss
new file mode 100644
index 000000000..86cb0b90e
--- /dev/null
+++ b/public/components/visualizations/charts/data_table/data_table.scss
@@ -0,0 +1,63 @@
+.pagination-wrapper {
+ display: flex;
+ justify-content: space-between;
+}
+.custom-pagination-container {
+ display: inline-flex;
+ padding: 10px 5px;
+}
+
+.custom-pagination-container li {
+ font-size: 14px;
+ padding: 0 5px;
+ display: flex;
+ align-items: flex-end;
+ font-weight: 600;
+}
+
+.custom-pagination-container .euiIcon:focus {
+ background: transparent !important;
+}
+.custom-pagination-container a:focus {
+ animation: none !important;
+}
+.custom-pagination-container a {
+ cursor: pointer;
+}
+
+.custom-pagination-container .disabled {
+ cursor: default;
+}
+.custom-pagination-container a {
+ color: #343741;
+}
+.dark-theme a {
+ color: #dfe5ef;
+}
+.custom-pagination-container .selected a {
+ color: #1ba9f5;
+ text-decoration: underline;
+ cursor: default;
+}
+
+.custom-overlay {
+ display: flex;
+ align-items: flex-start;
+ padding: 10px;
+ position: fixed;
+ top: 100px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 99999 !important;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+.custom-overlay-light {
+ background-color: #fff;
+}
+
+.custom-overlay-dark {
+ background-color: #16171c;
+}
diff --git a/public/components/visualizations/charts/data_table/data_table.tsx b/public/components/visualizations/charts/data_table/data_table.tsx
index 912a9dc2c..dfa40da13 100644
--- a/public/components/visualizations/charts/data_table/data_table.tsx
+++ b/public/components/visualizations/charts/data_table/data_table.tsx
@@ -3,102 +3,265 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState, useMemo, useCallback } from 'react';
-import { EuiInMemoryTable, EuiDataGrid } from '@elastic/eui';
+import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-export const DataTable = ({ visualizations }: any) => {
+// ag-data-grid
+import { AgGridReact } from 'ag-grid-react';
+import 'ag-grid-community/dist/styles/ag-grid.css';
+import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
+
+// grid elements
+import { CustomOverlay, RowConfigType, GridHeader } from './data_table_header';
+import { GridFooter } from './data_table_footer';
+
+// constants
+import { COLUMN_DEFAULT_MIN_WIDTH, HEADER_HEIGHT } from '../../../../../common/constants/explorer';
+import { IVisualizationContainerProps, IField } from '../../../../../common/types/explorer';
+import 'ag-grid-community/dist/styles/ag-theme-alpine-dark.css';
+
+// styles
+import './data_table.scss';
+
+const doubleValueGetter = (params) => {
+ return params.data[params.column.colId];
+};
+
+export const DataTable = ({ visualizations, layout, config }: any) => {
const {
- data: vizData,
- jsonData,
- metadata: { fields = [] },
- } = visualizations.data.rawVizData;
-
- const raw_data = [...jsonData];
-
- // Pagination
- const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
- const onChangeItemsPerPage = useCallback(
- (pageSize) =>
- setPagination((pagination) => ({
- ...pagination,
- pageSize,
- pageIndex: 0,
- })),
- [setPagination]
- );
- const onChangePage = useCallback(
- (pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })),
- [setPagination]
- );
+ data: {
+ defaultAxes,
+ indexFields,
+ query,
+ rawVizData: {
+ data: queriedVizData,
+ jsonData,
+ metadata: { fields = [] },
+ },
+ userConfigs: { dataConfig: { chartStyles = {} } = {} } = {},
+ } = {},
+ vis: visMetaData,
+ }: IVisualizationContainerProps = visualizations;
- // Sorting
- const [sortingColumns, setSortingColumns] = useState([]);
- const onSort = useCallback(
- (sortingColumns) => {
- setSortingColumns(sortingColumns);
- },
- [setSortingColumns]
- );
+ const enablePagination =
+ typeof chartStyles.enablePagination !== 'undefined'
+ ? chartStyles.enablePagination
+ : visualizations.vis.enablepagination;
- // Sort data
- let data = useMemo(() => {
- return [...raw_data].sort((a, b) => {
- for (let i = 0; i < sortingColumns.length; i++) {
- const column = sortingColumns[i];
- const aValue = a[column.id];
- const bValue = b[column.id];
+ const showTableHeader =
+ typeof chartStyles.showTableHeader !== 'undefined'
+ ? chartStyles.showTableHeader
+ : visualizations.vis.showtableheader;
- if (aValue < bValue) return column.direction === 'asc' ? -1 : 1;
- if (aValue > bValue) return column.direction === 'asc' ? 1 : -1;
- }
+ const colunmFilter =
+ typeof chartStyles.colunmFilter !== 'undefined'
+ ? chartStyles.colunmFilter
+ : visualizations.vis.colunmfilter;
- return 0;
- });
- }, [sortingColumns]);
+ const columnAlignment = chartStyles.columnAlignment || visualizations.vis.columnalignment;
- // Pagination
- data = useMemo(() => {
- const rowStart = pagination.pageIndex * pagination.pageSize;
- const rowEnd = Math.min(rowStart + pagination.pageSize, data.length);
- return data.slice(rowStart, rowEnd);
- }, [data, pagination]);
+ const columnWidth =
+ typeof chartStyles.columnWidth !== 'undefined'
+ ? chartStyles.columnWidth
+ : visualizations.vis.columnwidth;
- const columns = fields.map((field) => {
- return {
- id: field.name,
+ useEffect(() => {
+ document.addEventListener('keydown', hideGridFullScreenHandler);
+ return () => {
+ document.removeEventListener('keydown', hideGridFullScreenHandler);
};
- });
+ }, []);
- // Column visibility
- const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id));
+ // rows and columns
+ const rawData = [...jsonData];
- const renderCellValue = useMemo(() => {
- return ({ rowIndex, columnId }) => {
- let adjustedRowIndex = rowIndex;
+ const columns = useMemo(
+ () =>
+ fields.map((field: IField) => {
+ return {
+ lockVisible: true,
+ columnsMenuParams: {
+ suppressColumnFilter: true,
+ suppressColumnSelectAll: true,
+ suppressColumnExpandAll: true,
+ },
+ headerName: field.name,
+ field: field.name,
+ colId: field.name,
+ ...(field.type === 'double' && {
+ valueGetter: doubleValueGetter,
+ }),
+ };
+ }),
+ [fields]
+ );
- // If we are doing the pagination (instead of leaving that to the grid)
- // then the row index must be adjusted as `data` has already been pruned to the page size
- adjustedRowIndex = rowIndex - pagination.pageIndex * pagination.pageSize;
+ // ag-grid-react bindings
+ const gridRef = useRef();
+ const gridRefFullScreen = useRef();
+ const [pageSize, setPageSize] = useState(10);
+ const [columnVisibility, setColumnVisibility] = useState([]);
+ const [isFullScreen, setIsFullScreen] = useState(false);
+ const [selectedRowDensity, setSelectedRowDensity] = useState({
+ icon: 'tableDensityCompact',
+ height: 35,
+ selected: true,
+ });
+ // pagination
+ const [activePage, setActivePage] = useState(0);
+ const pageCount = Math.ceil(rawData.length / pageSize);
- return data.hasOwnProperty(adjustedRowIndex) ? data[adjustedRowIndex][columnId] : null;
+ const defaultColDef = useMemo(() => {
+ return {
+ sortable: true,
+ resizable: true,
+ filter: colunmFilter,
+ flex: 1,
+ suppressMenu: false,
+ minWidth: COLUMN_DEFAULT_MIN_WIDTH,
+ headerHeight: 400,
+ type: columnAlignment,
};
- }, [data, pagination.pageIndex, pagination.pageSize]);
+ }, [colunmFilter, columnAlignment]);
+
+ useEffect(() => {
+ if (!chartStyles.columnWidth) {
+ gridRef?.current?.api?.sizeColumnsToFit();
+ } else {
+ columns.forEach((col: any) =>
+ gridRef?.current?.columnApi?.setColumnWidth(col.field, Number(columnWidth))
+ );
+ }
+ }, [columnWidth, columns]);
+
+ const onPageSizeChanged = useCallback(
+ (val: number) => {
+ setPageSize(val);
+ gridRef.current.api.paginationSetPageSize(val);
+ setActivePage(0);
+ gridRef.current.api.paginationGoToPage(0);
+ if (isFullScreen) {
+ gridRefFullScreen.current.api.paginationSetPageSize(val);
+ gridRefFullScreen.current.api.paginationGoToPage(0);
+ }
+ },
+ [isFullScreen]
+ );
+
+ const selectDensityHandler = useCallback((value: RowConfigType) => {
+ setSelectedRowDensity({ ...value });
+ gridRef.current.api.forEachNode((rowNode) => {
+ if (rowNode.data) {
+ rowNode.setRowHeight(value.height);
+ }
+ });
+ gridRef.current.api.onRowHeightChanged();
+ }, []);
+
+ const columnVisiblityHandler = useCallback((visible: boolean, field: string) => {
+ const isExisting = columnVisibility.findIndex((column) => column === field);
+ if (visible) {
+ if (isExisting > -1) {
+ columnVisibility.splice(isExisting, 1);
+ gridRef?.current?.columnApi?.setColumnsVisible([field], true);
+ }
+ } else {
+ columnVisibility.push(field);
+ gridRef?.current?.columnApi?.setColumnsVisible([field], false);
+ }
+ setColumnVisibility([...columnVisibility]);
+ }, []);
+
+ const goToPage = ({ selected }: { selected: number }) => {
+ setActivePage(selected);
+ gridRef.current.api.paginationGoToPage(selected);
+ if (isFullScreen) {
+ gridRefFullScreen.current.api.paginationGoToPage(selected);
+ }
+ };
+
+ const setIsFullScreenHandler = (val: boolean) => {
+ setIsFullScreen(val);
+ };
+
+ const hideGridFullScreenHandler = (e: any) => {
+ if (e.key === 'Escape') {
+ setIsFullScreen(false);
+ }
+ };
return (
-
+ <>
+ {showTableHeader && (
+
+ )}
+ {
+ gridRef?.current?.api.setHeaderHeight(HEADER_HEIGHT);
+ }}
+ suppressFieldDotNotation // added for key contains dot operator
+ />
+ {enablePagination && (
+
+ )}
+ {isFullScreen && (
+
+
+
+ {
+ gridRefFullScreen?.current?.api.setHeaderHeight(HEADER_HEIGHT);
+ }}
+ // added for key contains dot operator
+ suppressFieldDotNotation
+ />
+
+
+
+
+
+
+ )}
+ >
);
};
diff --git a/public/components/visualizations/charts/data_table/data_table_footer.tsx b/public/components/visualizations/charts/data_table/data_table_footer.tsx
new file mode 100644
index 000000000..f8350ddd2
--- /dev/null
+++ b/public/components/visualizations/charts/data_table/data_table_footer.tsx
@@ -0,0 +1,126 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState } from 'react';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiPopover,
+ EuiIcon,
+ EuiContextMenuPanel,
+ EuiContextMenuItem,
+} from '@elastic/eui';
+
+// pagination
+import ReactPaginate from 'react-paginate';
+
+// theme
+import { uiSettingsService } from '../../../../../common/utils';
+
+// constants
+import {
+ GRID_PAGE_RANGE_DISPLAY,
+ GRID_PAGE_SIZES,
+} from '../../../../../common/constants/explorer';
+
+export const GridFooter = ({
+ onPageSizeChanged,
+ pageSize,
+ activePage,
+ goToPage,
+ pageCount,
+}: {
+ onPageSizeChanged: (val: number) => void;
+ goToPage: ({ selected }: { selected: number }) => void;
+ pageSize: number;
+ activePage: number;
+ pageCount: number;
+}) => {
+ return (
+
+
+
+
+
+ }
+ forcePage={activePage}
+ onPageChange={goToPage}
+ pageRangeDisplayed={GRID_PAGE_RANGE_DISPLAY}
+ pageCount={pageCount}
+ previousLabel={}
+ renderOnZeroPageCount={null}
+ />
+
+
+ );
+};
+
+export const PageSizePopover = ({
+ onPageSizeChanged,
+ pageSize,
+}: {
+ onPageSizeChanged: (size: number) => void;
+ pageSize: number;
+}) => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen);
+ const closePopover = () => setIsPopoverOpen(false);
+
+ const button = (
+
+ Rows per page: {pageSize}
+
+ );
+
+ const items = () => {
+ return GRID_PAGE_SIZES.map((i: number) => (
+ {
+ onPageSizeChanged(i);
+ closePopover();
+ }}
+ >
+ {i} rows
+
+ ));
+ };
+
+ return (
+
+
+
+ );
+};
diff --git a/public/components/visualizations/charts/data_table/data_table_header.tsx b/public/components/visualizations/charts/data_table/data_table_header.tsx
new file mode 100644
index 000000000..1c2d2b034
--- /dev/null
+++ b/public/components/visualizations/charts/data_table/data_table_header.tsx
@@ -0,0 +1,195 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, ReactChildren, ReactChild } from 'react';
+import {
+ EuiButtonIcon,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiPopover,
+ EuiSwitch,
+ EuiIcon,
+} from '@elastic/eui';
+
+// theme
+import { uiSettingsService } from '../../../../../common/utils';
+
+// constants
+import {
+ GRID_HEADER_COLUMN_MAX_WIDTH,
+ ROW_DENSITIES,
+} from '../../../../../common/constants/explorer';
+
+export interface RowConfigType {
+ icon: string;
+ height: number;
+ selected: boolean;
+}
+
+export const CustomOverlay = ({
+ children,
+}: {
+ children: ReactChild | ReactChild[] | ReactChildren | ReactChildren[];
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export const GridHeader = ({
+ isFullScreen,
+ setIsFullScreenHandler,
+ selectedRowDensity,
+ selectDensityHandler,
+ columnVisiblityHandler,
+ columns,
+ columnVisibility,
+}: {
+ isFullScreen: boolean;
+ setIsFullScreenHandler: (v: boolean) => void;
+ selectedRowDensity: any;
+ selectDensityHandler: (v: RowConfigType) => void;
+ columnVisiblityHandler: (visible: boolean, field: string) => void;
+ columns: any;
+ columnVisibility: any;
+}) => {
+ return (
+
+
+
+
+
+
+
+
+ setIsFullScreenHandler(true)}
+ >
+ Full screen
+
+
+ {isFullScreen && (
+ setIsFullScreenHandler(false)}
+ style={{ position: 'absolute', right: 20, cursor: 'pointer', top: 20 }}
+ />
+ )}
+
+ );
+};
+
+export const DensityPopover = ({
+ selectDensityHandler,
+ selectedDensity,
+}: {
+ selectedDensity: any;
+ selectDensityHandler: (data: RowConfigType) => void;
+}) => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen);
+ const closePopover = () => setIsPopoverOpen(false);
+
+ const button = (
+
+ Density
+
+ );
+
+ return (
+
+
+ {ROW_DENSITIES.map((i: RowConfigType, index: number) => (
+
+ selectDensityHandler(i)}
+ display={selectedDensity.icon === i.icon ? 'fill' : 'base'}
+ iconType={i.icon}
+ aria-label="Next"
+ />
+
+ ))}
+
+
+ );
+};
+
+export const ColumnVisiblityPopover = ({
+ columnVisibility,
+ columns,
+ columnVisiblityHandler,
+}: {
+ columns: any;
+ columnVisibility: any;
+ columnVisiblityHandler: (visible: boolean, field: string) => void;
+}) => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen);
+ const closePopover = () => setIsPopoverOpen(false);
+
+ const button = (
+
+ Columns
+
+ );
+
+ return (
+
+
+ {columns.map((i: any, index: number) => {
+ return (
+
+ {
+ columnVisiblityHandler(e.target.checked, i.field);
+ }}
+ compressed
+ />
+
+ );
+ })}
+
+
+ );
+};
diff --git a/public/components/visualizations/charts/data_table/data_table_type.ts b/public/components/visualizations/charts/data_table/data_table_type.ts
index e3f03764a..2ca11ace0 100644
--- a/public/components/visualizations/charts/data_table/data_table_type.ts
+++ b/public/components/visualizations/charts/data_table/data_table_type.ts
@@ -4,34 +4,122 @@
*/
import { DataTable } from './data_table';
-import { getPlotlyCategory } from '../shared/shared_configs';
+import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
import { LensIconChartDatatable } from '../../assets/chart_datatable';
+import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
+import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability';
+import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
+import {
+ InputFieldItem,
+ SwitchButton,
+ ConfigChartOptions,
+ ButtonGroupItem,
+} from './../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const COLUMN_WIDTH = 150;
export const createDatatableTypeDefinition = (params: any = {}) => ({
name: 'data_table',
type: 'data_table',
id: 'data_table',
- label: 'Data Table',
- fullLabel: 'Data Table',
- iconType: 'visTable',
+ label: 'Table View',
+ fulllabel: 'Table View',
+ icontype: 'visTable',
category: VIS_CATEGORY.BASICS,
- selection: {
- dataLoss: 'nothing',
- },
icon: LensIconChartDatatable,
- editorConfig: {
- editor: null,
- schemas: [
+ showtableheader: true,
+ enablepagination: true,
+ colunmfilter: false,
+ columnalignment: 'leftAligned',
+ columnwidth: COLUMN_WIDTH,
+ editorconfig: {
+ panelTabs: [
+ {
+ id: 'data-panel',
+ name: 'Data',
+ mapTo: 'dataConfig',
+ editor: VizDataPanel,
+ sections: [
+ {
+ id: 'chart-styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
+ schemas: [
+ {
+ title: 'Show table header',
+ name: 'Show table header',
+ component: SwitchButton,
+ mapTo: 'showTableHeader',
+ eleType: 'switchButton',
+ currentValue: true,
+ },
+ {
+ title: 'Enable pagination',
+ name: 'Enable pagination',
+ component: SwitchButton,
+ mapTo: 'enablePagination',
+ eleType: 'switchButton',
+ currentValue: true,
+ },
+ {
+ name: 'Column alignment',
+ component: ButtonGroupItem,
+ mapTo: 'columnAlignment',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Left', id: 'leftAligned' },
+ { name: 'Right', id: 'rightAligned' },
+ ],
+ defaultSelections: [{ name: 'Left', id: 'leftAligned' }],
+ },
+ },
+ {
+ title: 'Column Width',
+ name: 'Column Width',
+ component: InputFieldItem,
+ mapTo: 'columnWidth',
+ eleType: 'input',
+ currentValue: 150,
+ },
+ {
+ title: 'Column filter',
+ name: 'Column filter',
+ component: SwitchButton,
+ mapTo: 'colunmFilter',
+ eleType: 'switchButton',
+ currentValue: false,
+ },
+ ],
+ },
+ ],
+ },
{
- name: 'Columns',
- isSingleSelection: true,
- onChangeHandler: 'setXaxisSelections',
- component: null,
- mapTo: 'xaxis',
+ id: 'style-panel',
+ name: 'Layout',
+ mapTo: 'layoutConfig',
+ editor: ConfigEditor,
+ content: [],
+ },
+ {
+ id: 'availability-panel',
+ name: 'Availability',
+ mapTo: 'availabilityConfig',
+ editor: ConfigAvailability,
},
],
},
+ visconfig: {
+ layout: {
+ ...sharedConfigs.layout,
+ },
+ config: {
+ ...sharedConfigs.config,
+ },
+ isUniColor: false,
+ },
component: DataTable,
});
diff --git a/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx b/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx
deleted file mode 100644
index 150b93ab5..000000000
--- a/public/components/visualizations/charts/financial/candle_stick/candle_stick.tsx
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from 'react';
-
-import { Plt } from '../../../plotly/plot';
-
-export const CandleStick = ({ visualizations, layout, config }) => {
- const trace1 = {
- x: [
- '2017-01-04',
- '2017-01-05',
- '2017-01-06',
- '2017-01-09',
- '2017-01-10',
- '2017-01-11',
- '2017-01-12',
- '2017-01-13',
- '2017-01-17',
- '2017-01-18',
- '2017-01-19',
- '2017-01-20',
- '2017-01-23',
- '2017-01-24',
- '2017-01-25',
- '2017-01-26',
- '2017-01-27',
- '2017-01-30',
- '2017-01-31',
- '2017-02-01',
- '2017-02-02',
- '2017-02-03',
- '2017-02-06',
- '2017-02-07',
- '2017-02-08',
- '2017-02-09',
- '2017-02-10',
- '2017-02-13',
- '2017-02-14',
- '2017-02-15',
- ],
-
- close: [
- 116.019997,
- 116.610001,
- 117.910004,
- 118.989998,
- 119.110001,
- 119.75,
- 119.25,
- 119.040001,
- 120,
- 119.989998,
- 119.779999,
- 120,
- 120.080002,
- 119.970001,
- 121.879997,
- 121.940002,
- 121.949997,
- 121.629997,
- 121.349998,
- 128.75,
- 128.529999,
- 129.080002,
- 130.289993,
- 131.529999,
- 132.039993,
- 132.419998,
- 132.119995,
- 133.289993,
- 135.020004,
- 135.509995,
- ],
-
- decreasing: { line: { color: '#7F7F7F' } },
-
- high: [
- 116.510002,
- 116.860001,
- 118.160004,
- 119.43,
- 119.379997,
- 119.93,
- 119.300003,
- 119.620003,
- 120.239998,
- 120.5,
- 120.089996,
- 120.449997,
- 120.809998,
- 120.099998,
- 122.099998,
- 122.440002,
- 122.349998,
- 121.629997,
- 121.389999,
- 130.490005,
- 129.389999,
- 129.190002,
- 130.5,
- 132.089996,
- 132.220001,
- 132.449997,
- 132.940002,
- 133.820007,
- 135.089996,
- 136.270004,
- ],
-
- increasing: { line: { color: '#17BECF' } },
-
- line: { color: 'rgba(31,119,180,1)' },
-
- low: [
- 115.75,
- 115.809998,
- 116.470001,
- 117.940002,
- 118.300003,
- 118.599998,
- 118.209999,
- 118.809998,
- 118.220001,
- 119.709999,
- 119.370003,
- 119.730003,
- 119.769997,
- 119.5,
- 120.279999,
- 121.599998,
- 121.599998,
- 120.660004,
- 120.620003,
- 127.010002,
- 127.779999,
- 128.160004,
- 128.899994,
- 130.449997,
- 131.220001,
- 131.119995,
- 132.050003,
- 132.75,
- 133.25,
- 134.619995,
- ],
-
- open: [
- 115.849998,
- 115.919998,
- 116.779999,
- 117.949997,
- 118.769997,
- 118.739998,
- 118.900002,
- 119.110001,
- 118.339996,
- 120,
- 119.400002,
- 120.449997,
- 120,
- 119.550003,
- 120.419998,
- 121.669998,
- 122.139999,
- 120.93,
- 121.150002,
- 127.029999,
- 127.980003,
- 128.309998,
- 129.130005,
- 130.539993,
- 131.350006,
- 131.649994,
- 132.460007,
- 133.080002,
- 133.470001,
- 135.520004,
- ],
-
- type: 'candlestick',
- xaxis: 'x',
- yaxis: 'y',
- };
-
- const candleStickData = [trace1];
-
- const finalLayout = {
- dragmode: 'zoom',
- margin: {
- r: 10,
- t: 25,
- b: 40,
- l: 60,
- },
- showlegend: false,
- xaxis: {
- autorange: true,
- domain: [0, 1],
- range: ['2017-01-03 12:00', '2017-02-15 12:00'],
- rangeslider: { range: ['2017-01-03 12:00', '2017-02-15 12:00'] },
- title: 'Date',
- type: 'date',
- },
- yaxis: {
- autorange: true,
- domain: [0, 1],
- range: [114.609999778, 137.410004222],
- type: 'linear',
- },
- ...layout,
- };
-
- return ;
-};
diff --git a/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts b/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts
deleted file mode 100644
index df9dd2fa9..000000000
--- a/public/components/visualizations/charts/financial/candle_stick/candle_stick_type.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { CandleStick } from './candle_stick';
-import { getPlotlySharedConfigs, getPlotlyCategory } from '../../shared/shared_configs';
-import { LensIconChartBar } from '../../../assets/chart_bar';
-import { VizDataPanel } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
-import { ConfigEditor } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
-
-const sharedConfigs = getPlotlySharedConfigs();
-const VIS_CATEGORY = getPlotlyCategory();
-
-export interface BarTypeParams {}
-
-export const createCandleStickDefinition = (params: BarTypeParams = {}) => ({
- name: 'candle_stick',
- type: 'candle_stick',
- id: 'candle_stick',
- label: 'Candle Stick',
- fullLabel: 'Candle Stick',
- selection: {
- dataLoss: 'nothing',
- },
- category: VIS_CATEGORY.BASICS,
- icon: LensIconChartBar,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
- orientation: 'v',
- component: CandleStick,
- editorConfig: {
- panelTabs: [
- {
- id: 'data-panel',
- name: 'Data',
- mapTo: 'dataConfig',
- editor: VizDataPanel,
- sections: [
- {
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
- schemas: [
- {
- name: 'X-axis',
- isSingleSelection: true,
- component: null,
- mapTo: 'xaxis',
- },
- {
- name: 'Y-axis',
- isSingleSelection: false,
- component: null,
- mapTo: 'yaxis',
- },
- ],
- },
- ],
- },
- {
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
- },
- ],
- },
- visConfig: {
- layout: {
- ...sharedConfigs.layout,
- },
- config: {
- ...sharedConfigs.config,
- },
- isUniColor: false,
- },
-});
diff --git a/public/components/visualizations/charts/financial/gauge/gauge.tsx b/public/components/visualizations/charts/financial/gauge/gauge.tsx
index 711f4d9e2..02fecc3d0 100644
--- a/public/components/visualizations/charts/financial/gauge/gauge.tsx
+++ b/public/components/visualizations/charts/financial/gauge/gauge.tsx
@@ -3,103 +3,209 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useMemo } from 'react';
-import { indexOf } from 'lodash';
+import { find, isEmpty } from 'lodash';
import Plotly from 'plotly.js-dist';
+import React, { useMemo } from 'react';
+import {
+ AGGREGATIONS,
+ GROUPBY,
+ PLOTLY_GAUGE_COLUMN_NUMBER,
+ DEFAULT_GAUGE_CHART_PARAMETERS,
+} from '../../../../../../common/constants/explorer';
+import { IVisualizationContainerProps } from '../../../../../../common/types/explorer';
+import { PLOT_MARGIN } from '../../../../../../common/constants/shared';
+import { ThresholdUnitType } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds';
+import { EmptyPlaceholder } from '../../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { getPropName } from '../../../../event_analytics/utils/utils';
import { Plt } from '../../../plotly/plot';
-import { NUMERICAL_FIELDS } from '../../../../../../common/constants/shared';
-import { PLOTLY_GAUGE_COLUMN_NUMBER } from '../../../../../../common/constants/explorer';
+
+const {
+ GaugeTitleSize,
+ DisplayDefaultGauges,
+ OrientationDefault,
+ TickLength,
+ LegendPlacement,
+} = DEFAULT_GAUGE_CHART_PARAMETERS;
export const Gauge = ({ visualizations, layout, config }: any) => {
const {
- data,
- metadata: { fields },
- } = visualizations.data.rawVizData;
+ data: {
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ span = {},
+ chartStyles = {},
+ panelOptions = {},
+ thresholds = [],
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ },
+ layoutConfig = {},
+ },
+ },
+ vis: { icontype },
+ }: IVisualizationContainerProps = visualizations;
- const { dataConfig = {}, layoutConfig = {} } = visualizations.data.userConfigs;
+ const seriesLength = series.length;
+ const numberOfGauges = panelOptions.numberOfGauges || DisplayDefaultGauges;
- const series =
- dataConfig?.valueOptions && dataConfig?.valueOptions?.series
- ? dataConfig.valueOptions.series
- : [];
+ // style parameters
+ const titleSize = chartStyles.titleSize || GaugeTitleSize;
+ const valueSize = chartStyles.valueSize;
+ const showThresholdMarkers = chartStyles.showThresholdMarkers || false;
+ const showThresholdLabels = chartStyles.showThresholdLabels || false;
+ const orientation = chartStyles.orientation || OrientationDefault;
+ const legendPlacement = chartStyles.legendPlacement || LegendPlacement;
- const value =
- dataConfig?.valueOptions && dataConfig?.valueOptions?.value
- ? dataConfig.valueOptions.value
- : [];
+ let xaxes = dimensions;
+ const isEmptyPlot = !seriesLength || isEmpty(queriedVizData);
+ const timestampField = find(fields, (field) => field.type === 'timestamp');
+ if (span && span.time_field && timestampField) {
+ xaxes = [timestampField, ...xaxes];
+ }
- const thresholds = dataConfig?.thresholds || [];
+ if (isEmptyPlot) return ;
const gaugeData: Plotly.Data[] = useMemo(() => {
let calculatedGaugeData: Plotly.Data[] = [];
- if (series && series[0] && value && value[0]) {
- if (indexOf(NUMERICAL_FIELDS, series[0].type) > 0) {
+ // case 1,2: no dimension, single/multiple metrics
+ if (!xaxes.length && seriesLength >= 1) {
+ calculatedGaugeData = series.map((seriesItem: any) => {
+ return {
+ field_name: getPropName(seriesItem),
+ value: queriedVizData[getPropName(seriesItem)][0],
+ };
+ });
+ }
+
+ // case 3: multiple dimensions and multiple metrics
+ if (xaxes.length && seriesLength) {
+ const selectedDimensionsData = xaxes
+ .map((dimension: any) => {
+ return queriedVizData[dimension.name]
+ ? queriedVizData[dimension.name].slice(0, numberOfGauges)
+ : [];
+ })
+ .reduce((prev, cur) => {
+ return prev.map((i, j) => `${i}, ${cur[j]}`);
+ });
+ const selectedSeriesData = series.map((seriesItem: any) => {
+ const propValue = getPropName(seriesItem);
+ return queriedVizData[`${propValue}`]
+ ? queriedVizData[`${propValue}`].slice(0, numberOfGauges)
+ : [];
+ });
+
+ selectedSeriesData.map((seriesSlice: any, seriesSliceIndex: number) => {
calculatedGaugeData = [
- ...data[value[0].name].map((dimesionSlice, index) => ({
- field_name: dimesionSlice,
- value: data[series[0].name][index],
- })),
+ ...calculatedGaugeData,
+ ...seriesSlice.map((seriesSliceData: any, seriesSliceDataIndex: number) => {
+ return {
+ field_name: `${selectedDimensionsData[seriesSliceDataIndex]}, ${getPropName(
+ series[seriesSliceIndex]
+ )}`,
+ value: seriesSliceData,
+ };
+ }),
];
- } else {
- value.map((val) => {
- const selectedSeriesIndex = indexOf(data[series[0].name], val.name);
- fields.map((field) => {
- if (field.name !== series[0].name) {
- calculatedGaugeData.push({
- field_name: field.name,
- value: data[field.name][selectedSeriesIndex],
- });
- }
- });
- });
- }
+ });
+ }
- return calculatedGaugeData.map((gauge, index) => {
- return {
- type: 'indicator',
- mode: 'gauge+number+delta',
- value: gauge.value || 0,
- title: {
- text: gauge.field_name,
- font: { size: 14 },
+ return calculatedGaugeData.map((gauge, index) => {
+ return {
+ type: 'indicator',
+ mode: 'gauge+number+delta',
+ value: gauge.value || 0,
+ title: {
+ text: gauge.field_name,
+ font: { size: titleSize },
+ align: legendPlacement,
+ },
+ ...(valueSize && {
+ number: {
+ font: {
+ size: valueSize,
+ },
},
- domain: {
- row: Math.floor(index / PLOTLY_GAUGE_COLUMN_NUMBER),
- column: index % PLOTLY_GAUGE_COLUMN_NUMBER,
- },
- gauge: {
- ...(thresholds && {
+ }),
+ domain: {
+ ...(orientation === 'auto' || orientation === 'h'
+ ? {
+ row: Math.floor(index / PLOTLY_GAUGE_COLUMN_NUMBER),
+ column: index % PLOTLY_GAUGE_COLUMN_NUMBER,
+ }
+ : {
+ column: Math.floor(index / PLOTLY_GAUGE_COLUMN_NUMBER),
+ row: index % PLOTLY_GAUGE_COLUMN_NUMBER,
+ }),
+ },
+ gauge: {
+ ...(showThresholdMarkers &&
+ thresholds &&
+ thresholds.length && {
threshold: {
line: { color: thresholds[0]?.color || 'red', width: 4 },
thickness: 0.75,
value: thresholds[0]?.value || 0,
},
}),
- },
- };
- });
- }
- return calculatedGaugeData;
- }, [series, value, data, fields, thresholds]);
+ // threshold labels
+ ...(showThresholdLabels && thresholds && thresholds.length
+ ? {
+ axis: {
+ ticktext: [gauge.value, ...thresholds.map((t: ThresholdUnitType) => t.name)],
+ tickvals: [gauge.value, ...thresholds.map((t: ThresholdUnitType) => t.value)],
+ ticklen: TickLength,
+ },
+ }
+ : {}),
+ },
+ };
+ });
+ }, [
+ xaxes,
+ series,
+ queriedVizData,
+ fields,
+ thresholds,
+ showThresholdMarkers,
+ orientation,
+ showThresholdLabels,
+ titleSize,
+ valueSize,
+ ]);
const mergedLayout = useMemo(() => {
const isAtleastOneFullRow = Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) > 0;
return {
grid: {
- rows: Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) + 1,
- columns: isAtleastOneFullRow ? PLOTLY_GAUGE_COLUMN_NUMBER : gaugeData.length,
+ ...(orientation === 'auto' || orientation === 'h'
+ ? {
+ rows: Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) + 1,
+ columns: isAtleastOneFullRow ? PLOTLY_GAUGE_COLUMN_NUMBER : gaugeData.length,
+ }
+ : {
+ columns: Math.floor(gaugeData.length / PLOTLY_GAUGE_COLUMN_NUMBER) + 1,
+ rows: isAtleastOneFullRow ? PLOTLY_GAUGE_COLUMN_NUMBER : gaugeData.length,
+ }),
pattern: 'independent',
},
...layout,
...(layoutConfig.layout && layoutConfig.layout),
- title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '',
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ margin: {
+ ...PLOT_MARGIN,
+ t: 100,
+ },
};
- }, [layout, gaugeData.length, layoutConfig.layout, dataConfig?.panelOptions?.title]);
+ }, [layout, gaugeData.length, layoutConfig.layout, panelOptions.title, orientation]);
const mergedConfigs = {
...config,
...(layoutConfig.config && layoutConfig.config),
};
-
return ;
};
diff --git a/public/components/visualizations/charts/financial/gauge/gauge_type.ts b/public/components/visualizations/charts/financial/gauge/gauge_type.ts
index e5e91b7db..2da38fefd 100644
--- a/public/components/visualizations/charts/financial/gauge/gauge_type.ts
+++ b/public/components/visualizations/charts/financial/gauge/gauge_type.ts
@@ -9,54 +9,102 @@ import { LensIconChartLine } from '../../../assets/chart_line';
import { VizDataPanel } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
import {
- ConfigValueOptions,
ConfigThresholds,
- ConfigGaugeValueOptions,
+ InputFieldItem,
+ SwitchButton,
+ ConfigChartOptions,
+ ButtonGroupItem,
} from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { DEFAULT_GAUGE_CHART_PARAMETERS } from '../../../../../../common/constants/explorer';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const { ThresholdsMaxLimit } = DEFAULT_GAUGE_CHART_PARAMETERS;
export const createGaugeTypeDefinition = (params: any = {}) => ({
name: 'Gauge',
type: 'indicator',
id: 'gauge',
label: 'Gauge',
- fullLabel: 'Gauge',
- iconType: 'visGauge',
+ fulllabel: 'Gauge',
+ icontype: 'visGauge',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
icon: LensIconChartLine,
- valueSeries: 'yaxis',
- editorConfig: {
+ valueseries: 'yaxis',
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigGaugeValueOptions,
- mapTo: 'valueOptions',
+ id: 'chart-styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
schemas: [
{
- name: 'Series',
- isSingleSelection: true,
- onChangeHandler: 'setXaxisSelections',
- component: null,
- mapTo: 'series',
+ name: 'Orientation',
+ component: ButtonGroupItem,
+ mapTo: 'orientation',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Auto', id: 'auto' },
+ { name: 'Vertical', id: 'v' },
+ { name: 'Horizontal', id: 'h' },
+ ],
+ defaultSelections: [{ name: 'Auto', id: 'auto' }],
+ },
},
{
- name: 'Value',
- isSingleSelection: false,
- onChangeHandler: 'setYaxisSelections',
- component: null,
- mapTo: 'value',
+ name: 'Legend placement',
+ component: ButtonGroupItem,
+ mapTo: 'legendPlacement',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Center', id: 'center' },
+ { name: 'Right', id: 'right' },
+ { name: 'Left', id: 'left' },
+ ],
+ defaultSelections: [{ name: 'Center', id: 'center' }],
+ },
+ },
+ {
+ title: 'Title size',
+ name: 'Title size',
+ component: InputFieldItem,
+ mapTo: 'titleSize',
+ eleType: 'input',
+ },
+ {
+ title: 'Value size',
+ name: 'Value size',
+ component: InputFieldItem,
+ mapTo: 'valueSize',
+ eleType: 'input',
+ },
+ {
+ title: 'Show threshold labels',
+ name: 'Show threshold labels',
+ component: SwitchButton,
+ mapTo: 'showThresholdLabels',
+ eleType: 'switchButton',
+ currentValue: false,
+ },
+ {
+ title: 'Show threshold markers',
+ name: 'Show threshold markers',
+ component: SwitchButton,
+ mapTo: 'showThresholdMarkers',
+ eleType: 'switchButton',
+ currentValue: true,
},
],
},
@@ -67,6 +115,9 @@ export const createGaugeTypeDefinition = (params: any = {}) => ({
mapTo: 'thresholds',
defaultState: [],
schemas: [],
+ props: {
+ maxLimit: ThresholdsMaxLimit,
+ },
},
],
},
@@ -79,7 +130,7 @@ export const createGaugeTypeDefinition = (params: any = {}) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
},
diff --git a/public/components/visualizations/charts/helpers/viz_types.ts b/public/components/visualizations/charts/helpers/viz_types.ts
index 4b7eb2332..bc71c36d1 100644
--- a/public/components/visualizations/charts/helpers/viz_types.ts
+++ b/public/components/visualizations/charts/helpers/viz_types.ts
@@ -4,9 +4,25 @@
*/
import { isEmpty, take } from 'lodash';
+import {
+ AGGREGATIONS,
+ CUSTOM_LABEL,
+ GROUPBY,
+ PARENTFIELDS,
+ SIMILAR_VIZ_TYPES,
+ TIMESTAMP,
+ TIME_INTERVAL_OPTIONS,
+} from '../../../../../common/constants/explorer';
+import { VIS_CHART_TYPES } from '../../../../../common/constants/shared';
+import { QueryManager } from '../../../../../common/query_manager';
+import {
+ ExplorerData,
+ IField,
+ IQuery,
+ IVisualizationContainerProps,
+} from '../../../../../common/types/explorer';
import { getVisType } from '../vis_types';
-import { IVisualizationContainerProps, IField, IQuery } from '../../../../../common/types/explorer';
-
+import { statsChunk } from '../../../../../common/query_manager/ast/types/stats';
interface IVizContainerProps {
vizId: string;
appData?: { fromApp: boolean };
@@ -18,16 +34,230 @@ interface IVizContainerProps {
xaxis: IField[];
yaxis: IField[];
};
+ explorer?: ExplorerData;
}
-const getDefaultXYAxisLabels = (vizFields: string[]) => {
+const initialDimensionEntry = {
+ label: '',
+ name: '',
+};
+
+const initialSeriesEntry = {
+ [CUSTOM_LABEL]: '',
+ label: '',
+ name: '',
+ aggregation: 'count',
+};
+
+const initialEntryTreemap = { label: '', name: '' };
+
+const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => {
if (isEmpty(vizFields)) return {};
+ const vizFieldsWithLabel: Array<{ [key: string]: string }> = vizFields.map((vizField) => ({
+ ...vizField,
+ label: vizField.name,
+ }));
+
+ const mapXaxis = (): Array<{ [key: string]: string }> => {
+ const xaxis = vizFieldsWithLabel.filter((field) => field.type === 'timestamp');
+ return visName === VIS_CHART_TYPES.Line
+ ? xaxis.length === 0
+ ? [initialDimensionEntry]
+ : xaxis
+ : [vizFieldsWithLabel[vizFieldsWithLabel.length - 1]];
+ };
+
+ const mapYaxis = (): Array<{ [key: string]: string }> =>
+ visName === VIS_CHART_TYPES.Line
+ ? vizFieldsWithLabel.filter((field) => field.type !== 'timestamp')
+ : take(
+ vizFieldsWithLabel,
+ vizFieldsWithLabel.length - 1 > 0 ? vizFieldsWithLabel.length - 1 : 1
+ ) || [];
+
+ return { xaxis: mapXaxis(), yaxis: mapYaxis() };
+};
+
+const getStandardedOuiField = (name?: string, type?: string) => ({
+ name,
+ label: name,
+ type,
+});
+
+const getStandardUnitField = (name?: string, value?: string) => ({
+ name,
+ label: name,
+ value,
+});
+
+const getSpanValue = (statsTokens: statsChunk) => {
+ const fieldInfo = statsTokens.groupby?.span?.span_expression?.field;
+ const timeUnit = TIME_INTERVAL_OPTIONS.find(
+ (time_unit) => time_unit.value === statsTokens.groupby?.span?.span_expression?.time_unit
+ );
return {
- xaxis: [vizFields[vizFields.length - 1]] || [],
- yaxis: take(vizFields, vizFields.length - 1 > 0 ? vizFields.length - 1 : 1) || [],
+ span: {
+ time_field: statsTokens.groupby?.span?.span_expression?.field
+ ? [getStandardedOuiField(fieldInfo, TIMESTAMP)]
+ : [],
+ interval: statsTokens.groupby?.span?.span_expression?.literal_value ?? '0',
+ unit: statsTokens.groupby?.span?.span_expression?.time_unit
+ ? [getStandardUnitField(timeUnit?.text, timeUnit?.value)]
+ : [],
+ },
};
};
+const defaultUserConfigs = (queryString, visualizationName: string) => {
+ let tempUserConfigs = {};
+ const qm = new QueryManager();
+ const statsTokens = qm.queryParser().parse(queryString.rawQuery).getStats();
+ if (!statsTokens) {
+ tempUserConfigs = {
+ [AGGREGATIONS]: [],
+ [GROUPBY]: [],
+ };
+ } else {
+ tempUserConfigs = {
+ ...(statsTokens.groupby !== '' &&
+ statsTokens.groupby?.span !== null &&
+ getSpanValue(statsTokens)),
+ };
+ if (visualizationName === VIS_CHART_TYPES.LogsView) {
+ const dimensions = statsTokens.aggregations
+ .map((agg) => {
+ const logViewField = `${agg.function.name}(${agg.function.value_expression})` ?? '';
+ return {
+ label: logViewField,
+ name: logViewField,
+ };
+ })
+ .concat(
+ statsTokens.groupby.group_fields?.map((agg) => ({
+ label: agg.name ?? '',
+ name: agg.name ?? '',
+ }))
+ );
+ if (statsTokens.groupby.span !== null) {
+ const { field, literal_value, time_unit } = statsTokens.groupby.span.span_expression;
+ const timespanField = `span(${field},${literal_value}${time_unit})`;
+ dimensions.push({
+ label: timespanField,
+ name: timespanField,
+ });
+ }
+ tempUserConfigs = {
+ ...tempUserConfigs,
+ [AGGREGATIONS]: [],
+ [GROUPBY]: dimensions,
+ };
+ } else if (visualizationName === VIS_CHART_TYPES.HeatMap) {
+ tempUserConfigs = {
+ ...tempUserConfigs,
+ [GROUPBY]: [],
+ [AGGREGATIONS]: [],
+ };
+ } else {
+ tempUserConfigs = {
+ ...tempUserConfigs,
+ [AGGREGATIONS]: statsTokens.aggregations.map((agg) => ({
+ [CUSTOM_LABEL]: agg[CUSTOM_LABEL],
+ label: agg.function?.value_expression,
+ name: agg.function?.value_expression,
+ aggregation: agg.function?.name,
+ })),
+ [GROUPBY]: statsTokens.groupby?.group_fields?.map((dimension) => ({
+ label: dimension.name ?? '',
+ name: dimension.name ?? '',
+ })),
+ };
+ }
+ }
+ return tempUserConfigs;
+};
+
+const getUserConfigs = (
+ userSelectedConfigs: object,
+ vizFields: IField[],
+ visName: string,
+ query
+) => {
+ let configOfUser = userSelectedConfigs;
+ const axesData = getDefaultXYAxisLabels(vizFields, visName);
+ if (!(userSelectedConfigs.dataConfig?.GROUPBY || userSelectedConfigs.dataConfig?.AGGREGATIONS)) {
+ switch (visName) {
+ case VIS_CHART_TYPES.HeatMap:
+ configOfUser = {
+ ...userSelectedConfigs,
+ dataConfig:
+ userSelectedConfigs?.dataConfig === undefined
+ ? { ...defaultUserConfigs(query, visName) }
+ : {
+ ...userSelectedConfigs?.dataConfig,
+ },
+ };
+ break;
+ case VIS_CHART_TYPES.TreeMap:
+ configOfUser = {
+ ...userSelectedConfigs,
+ dataConfig: {
+ ...userSelectedConfigs?.dataConfig,
+ [GROUPBY]: [
+ {
+ childField: { ...(axesData.xaxis ? axesData.xaxis[0] : initialEntryTreemap) },
+ parentFields:
+ userSelectedConfigs?.dataConfig !== undefined &&
+ userSelectedConfigs.dataConfig[GROUPBY]?.length > 0
+ ? [...userSelectedConfigs.dataConfig[GROUPBY][0][PARENTFIELDS]]
+ : [],
+ },
+ ],
+ [AGGREGATIONS]: [
+ { valueField: { ...(axesData.yaxis ? axesData.yaxis[0] : initialEntryTreemap) } },
+ ],
+ },
+ };
+ break;
+ case VIS_CHART_TYPES.Histogram:
+ configOfUser = {
+ ...userSelectedConfigs,
+ dataConfig: {
+ ...userSelectedConfigs?.dataConfig,
+ [GROUPBY]: [{ bucketSize: '', bucketOffset: '' }],
+ [AGGREGATIONS]: [],
+ },
+ };
+ break;
+ case VIS_CHART_TYPES.LogsView:
+ configOfUser = {
+ ...userSelectedConfigs,
+ dataConfig: {
+ ...userSelectedConfigs?.dataConfig,
+ ...defaultUserConfigs(query, visName),
+ },
+ };
+ break;
+ default:
+ configOfUser = {
+ ...userSelectedConfigs,
+ dataConfig: {
+ ...userSelectedConfigs?.dataConfig,
+ ...defaultUserConfigs(query, visName),
+ },
+ };
+ break;
+ }
+ }
+ return isEmpty(configOfUser) ? userSelectedConfigs : configOfUser;
+};
+
+export const getVisTypeData = (vizId: string) => {
+ if (SIMILAR_VIZ_TYPES.includes(vizId)) {
+ return getVisType(vizId, { type: vizId });
+ }
+ return getVisType(vizId);
+};
+
export const getVizContainerProps = ({
vizId,
rawVizData = {},
@@ -35,20 +265,29 @@ export const getVizContainerProps = ({
indexFields = {},
userConfigs = {},
appData = {},
+ explorer = { explorerData: { jsonData: [], jsonDataAll: [] } },
}: IVizContainerProps): IVisualizationContainerProps => {
+ const getVisTypeData = () =>
+ SIMILAR_VIZ_TYPES.includes(vizId as VIS_CHART_TYPES)
+ ? { ...getVisType(vizId, { type: vizId }) }
+ : { ...getVisType(vizId) };
+
return {
data: {
appData: { ...appData },
rawVizData: { ...rawVizData },
query: { ...query },
indexFields: { ...indexFields },
- userConfigs: { ...userConfigs },
+ userConfigs: {
+ ...userConfigs,
+ },
defaultAxes: {
- ...getDefaultXYAxisLabels(rawVizData?.metadata?.fields),
+ ...getDefaultXYAxisLabels(rawVizData?.metadata?.fields, getVisTypeData(vizId).name),
},
+ explorer: { ...explorer },
},
vis: {
- ...getVisType(vizId),
+ ...getVisTypeData(vizId),
},
};
};
diff --git a/public/components/visualizations/charts/histogram/histogram.tsx b/public/components/visualizations/charts/histogram/histogram.tsx
index 28c9adb96..6b8d8fbe1 100644
--- a/public/components/visualizations/charts/histogram/histogram.tsx
+++ b/public/components/visualizations/charts/histogram/histogram.tsx
@@ -3,40 +3,111 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
-import { take, merge, isEmpty } from 'lodash';
+import { isEmpty, take } from 'lodash';
+import React, { useMemo } from 'react';
+import { GROUPBY } from '../../../../../common/constants/explorer';
+import {
+ DEFAULT_CHART_STYLES,
+ FILLOPACITY_DIV_FACTOR,
+ PLOTLY_COLOR,
+ VIS_CHART_TYPES,
+ PLOT_MARGIN,
+} from '../../../../../common/constants/shared';
+import { IVisualizationContainerProps } from '../../../../../common/types/explorer';
+import { hexToRgb } from '../../../../components/event_analytics/utils/utils';
import { Plt } from '../../plotly/plot';
-import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
export const Histogram = ({ visualizations, layout, config }: any) => {
- const { vis } = visualizations;
+ const { LineWidth, FillOpacity, LegendPosition, ShowLegend } = DEFAULT_CHART_STYLES;
const {
- data = {},
- metadata: { fields },
- } = visualizations.data.rawVizData;
- const { defaultAxes } = visualizations.data.defaultAxes;
- const { xaxis = null, yaxis = null } = visualizations.data.userConfigs;
+ data: {
+ defaultAxes,
+ indexFields,
+ query,
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ chartStyles = {},
+ legend = {},
+ tooltipOptions = {},
+ colorTheme = [],
+ panelOptions = {},
+ [GROUPBY]: dimensions = [],
+ },
+ layoutConfig = {},
+ },
+ },
+ vis: visMetaData,
+ }: IVisualizationContainerProps = visualizations;
+
const lastIndex = fields.length - 1;
+ const lineWidth = chartStyles.lineWidth || LineWidth;
+ const showLegend = legend.showLegend && legend.showLegend !== ShowLegend ? false : true;
+ const legendPosition = legend.position || LegendPosition;
+ const fillOpacity = (chartStyles.fillOpacity || FillOpacity) / FILLOPACITY_DIV_FACTOR;
+ const tooltipMode =
+ tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show';
+ const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all';
+ const valueSeries = defaultAxes?.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1);
- let valueSeries;
- if (!isEmpty(xaxis) && !isEmpty(yaxis)) {
- valueSeries = [
- ...visualizations?.data?.userConfigs[vis.seriesAxis].map((item) => ({
- ...item,
- name: item.label,
- })),
- ];
- } else {
- valueSeries = defaultAxes?.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1);
+ const xbins: any = {};
+ if (dimensions && dimensions[0]?.bucketSize) {
+ xbins.size = dimensions[0]?.bucketSize;
}
+ if (dimensions && dimensions[0]?.bucketOffset) {
+ xbins.start = dimensions[0]?.bucketOffset;
+ }
+
+ const selectedColorTheme = (field: string, index: number, opacity?: number) => {
+ let newColor;
+ if (colorTheme && colorTheme.length !== 0) {
+ newColor = colorTheme.find((colorSelected) => colorSelected.name.name === field);
+ }
+ return hexToRgb(newColor ? newColor.color : PLOTLY_COLOR[index % PLOTLY_COLOR.length], opacity);
+ };
+
+ const hisValues = useMemo(
+ () =>
+ valueSeries.map((field: any, index: number) => ({
+ x: queriedVizData[field.name],
+ type: VIS_CHART_TYPES.Histogram,
+ name: field.name,
+ hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
+ marker: {
+ color: selectedColorTheme(field.name, index, fillOpacity),
+ line: {
+ color: selectedColorTheme(field.name, index),
+ width: lineWidth,
+ },
+ },
+ xbins: !isEmpty(xbins) ? xbins : undefined,
+ })),
+ [valueSeries, queriedVizData, fillOpacity, lineWidth, xbins, selectedColorTheme]
+ );
+
+ const mergedLayout = {
+ ...layout,
+ ...(layoutConfig.layout && layoutConfig.layout),
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ barmode: 'group',
+ legend: {
+ ...layout.legend,
+ orientation: legendPosition,
+ },
+ showlegend: showLegend,
+ margin: PLOT_MARGIN,
+ };
- const hisValues = valueSeries.map((field: any) => {
- return {
- x: data[xaxis ? xaxis[0]?.label : fields[lastIndex].name],
- type: 'histogram',
- name: field.name,
- };
- });
+ const mergedConfigs = useMemo(
+ () => ({
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
+ }),
+ [config, layoutConfig.config]
+ );
- return ;
+ return ;
};
diff --git a/public/components/visualizations/charts/histogram/histogram_type.ts b/public/components/visualizations/charts/histogram/histogram_type.ts
index 3e6e0d967..d7d1cb577 100644
--- a/public/components/visualizations/charts/histogram/histogram_type.ts
+++ b/public/components/visualizations/charts/histogram/histogram_type.ts
@@ -5,51 +5,113 @@
import { Histogram } from './histogram';
import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
-import { LensIconChartLine } from '../../assets/chart_line';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import {
+ ConfigChartOptions,
+ ConfigLegend,
+ SliderConfig,
+ ConfigColorTheme,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const { LineWidth, FillOpacity, ShowLegend, LegendPosition } = DEFAULT_CHART_STYLES;
export const createHistogramVisDefinition = (params = {}) => ({
name: 'histogram',
type: 'histogram',
id: 'histogram',
label: 'Histogram',
- fullLabel: 'Histogram',
+ fulllabel: 'Histogram',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
- valueSeries: 'yaxis',
- icon: LensIconChartLine,
- editorConfig: {
+ valueseries: 'yaxis',
+ icontype: 'visArea',
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ id: 'chart-styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
schemas: [
{
- name: 'X-axis',
- isSingleSelection: true,
+ name: 'Line width',
+ component: SliderConfig,
+ mapTo: 'lineWidth',
+ defaultState: LineWidth,
+ eleType: 'slider',
+ props: {
+ max: 10,
+ },
+ },
+ {
+ name: 'Fill opacity',
+ component: SliderConfig,
+ mapTo: 'fillOpacity',
+ defaultState: FillOpacity,
+ eleType: 'slider',
+ props: {
+ max: 100,
+ },
+ },
+ ],
+ },
+ {
+ id: 'color-theme',
+ name: 'Color theme',
+ editor: ConfigColorTheme,
+ mapTo: 'colorTheme',
+ schemas: [],
+ },
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Dimension', id: 'x' },
+ { name: 'Series', id: 'y' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
+ {
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
+ schemas: [
+ {
+ name: 'Show legend',
+ mapTo: 'showLegend',
component: null,
- mapTo: 'xaxis',
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: ShowLegend }],
+ },
},
{
- name: 'Y-axis',
- isSingleSelection: false,
+ name: 'Position',
+ mapTo: 'position',
component: null,
- mapTo: 'yaxis',
+ props: {
+ options: [
+ { name: 'Right', id: 'v' },
+ { name: 'Bottom', id: 'h' },
+ ],
+ defaultSelections: [{ name: 'Right', id: LegendPosition }],
+ },
},
],
},
@@ -64,7 +126,7 @@ export const createHistogramVisDefinition = (params = {}) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
},
diff --git a/public/components/visualizations/charts/lines/line.tsx b/public/components/visualizations/charts/lines/line.tsx
index 4ce620efd..d669c60ed 100644
--- a/public/components/visualizations/charts/lines/line.tsx
+++ b/public/components/visualizations/charts/lines/line.tsx
@@ -3,104 +3,247 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { find, isEmpty, last, forEach } from 'lodash';
import React, { useMemo } from 'react';
-import { take, isEmpty, last } from 'lodash';
-import { Plt } from '../../plotly/plot';
+import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer';
+import {
+ DEFAULT_CHART_STYLES,
+ FILLOPACITY_DIV_FACTOR,
+ PLOTLY_COLOR,
+ VIS_CHART_TYPES,
+ PLOT_MARGIN,
+} from '../../../../../common/constants/shared';
+import { IVisualizationContainerProps } from '../../../../../common/types/explorer';
+import { hexToRgb } from '../../../../components/event_analytics/utils/utils';
import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability';
import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds';
+import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { Plt } from '../../plotly/plot';
+import { transformPreprocessedDataToTraces, preprocessJsonData } from '../shared/common';
export const Line = ({ visualizations, layout, config }: any) => {
const {
- data = {},
- metadata: { fields },
- } = visualizations.data.rawVizData;
- const { defaultAxes } = visualizations.data;
+ DefaultModeLine,
+ Interpolation,
+ LineWidth,
+ FillOpacity,
+ MarkerSize,
+ LegendPosition,
+ ShowLegend,
+ DefaultModeScatter,
+ LabelAngle,
+ } = DEFAULT_CHART_STYLES;
+
const {
- dataConfig = {},
- layoutConfig = {},
- availabilityConfig = {},
- } = visualizations?.data?.userConfigs;
- const xaxis =
- dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.xaxis : [];
- const yaxis =
- dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.yaxis : [];
+ data: {
+ rawVizData: {
+ data: queriedVizData,
+ jsonData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ chartStyles = {},
+ legend = {},
+ span = {},
+ colorTheme = [],
+ thresholds = [],
+ tooltipOptions = {},
+ panelOptions = {},
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ breakdowns = [],
+ } = {},
+ layoutConfig = {},
+ availabilityConfig = {},
+ } = {},
+ },
+ vis: { icontype, name },
+ }: IVisualizationContainerProps = visualizations;
+
+ const tooltipMode =
+ tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show';
+ const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all';
const lastIndex = fields.length - 1;
+ const visType: string = name;
const mode =
- dataConfig?.chartOptions && dataConfig.chartOptions.mode && dataConfig.chartOptions.mode[0]
- ? dataConfig.chartOptions.mode[0].modeId
- : 'line';
-
- let valueSeries;
- if (!isEmpty(xaxis) && !isEmpty(yaxis)) {
- valueSeries = [...yaxis];
- } else {
- valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1);
- }
+ chartStyles.style || (visType === VIS_CHART_TYPES.Line ? DefaultModeLine : DefaultModeScatter);
+ const lineShape = chartStyles.interpolation || Interpolation;
+ const lineWidth = chartStyles.lineWidth || LineWidth;
+ const showLegend = !(legend.showLegend && legend.showLegend !== ShowLegend);
+ const legendPosition = legend.position || LegendPosition;
+ const markerSize = chartStyles.pointSize || MarkerSize;
+ const fillOpacity =
+ chartStyles.fillOpacity !== undefined
+ ? chartStyles.fillOpacity / FILLOPACITY_DIV_FACTOR
+ : FillOpacity / FILLOPACITY_DIV_FACTOR;
+ const tickAngle = chartStyles.rotateLabels || LabelAngle;
+ const labelSize = chartStyles.labelSize;
+ const legendSize = legend.legendSize;
+ const getSelectedColorTheme = (field: any, index: number) =>
+ (colorTheme.length > 0 &&
+ 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];
+
+ if (!timestampField || isEmpty(series)) return ;
+
+ const addStylesToTraces = (traces, traceStyles) => {
+ const { fillOpacity, tooltipMode, tooltipText, lineWidth, lineShape, markerSize } = traceStyles;
+ return traces.map((trace, idx: number) => {
+ const selectedColor = getSelectedColorTheme(trace.aggName, idx);
+ const fillColor = hexToRgb(selectedColor, fillOpacity);
- const [calculatedLayout, lineValues] = useMemo(() => {
- let calculatedLineValues = valueSeries.map((field: any) => {
return {
- x: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name],
- y: data[field.name],
+ ...trace,
+ hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
type: 'line',
- name: field.name,
mode,
+ ...{
+ fill: 'tozeroy',
+ fillcolor: fillColor,
+ },
+ line: {
+ shape: lineShape,
+ width: lineWidth,
+ color: selectedColor,
+ },
+ marker: {
+ size: markerSize,
+ ...{
+ color: fillColor,
+ line: {
+ color: selectedColor,
+ width: lineWidth,
+ },
+ },
+ },
};
});
+ };
+
+ let lines = useMemo(() => {
+ const visConfig = {
+ dimensions,
+ series,
+ breakdowns,
+ span,
+ };
+ const traceStyles = {
+ fillOpacity,
+ tooltipMode,
+ tooltipText,
+ lineShape,
+ lineWidth,
+ markerSize,
+ };
+
+ return addStylesToTraces(
+ transformPreprocessedDataToTraces(preprocessJsonData(jsonData, visConfig), visConfig),
+ traceStyles
+ );
+ }, [
+ chartStyles,
+ jsonData,
+ dimensions,
+ series,
+ span,
+ breakdowns,
+ panelOptions,
+ tooltipOptions,
+ ]);
- const mergedLayout = {
+ const mergedLayout = useMemo(() => {
+ const axisLabelsStyle = {
+ automargin: true,
+ tickfont: {
+ ...(labelSize && {
+ size: labelSize,
+ }),
+ },
+ };
+
+ return {
...layout,
- ...layoutConfig.layout,
- title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '',
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ legend: {
+ ...layout.legend,
+ orientation: legendPosition,
+ ...(legendSize && {
+ font: {
+ size: legendSize,
+ },
+ }),
+ },
+ autosize: true,
+ xaxis: {
+ tickangle: tickAngle,
+ ...axisLabelsStyle,
+ },
+ yaxis: {
+ ...axisLabelsStyle,
+ },
+ showlegend: showLegend,
+ margin: PLOT_MARGIN,
};
+ }, [visualizations]);
- if (dataConfig.thresholds || availabilityConfig.level) {
- const thresholdTraces = {
- x: [],
- y: [],
- mode: 'text',
- text: [],
- };
- const thresholds = dataConfig.thresholds ? dataConfig.thresholds : [];
- const levels = availabilityConfig.level ? availabilityConfig.level : [];
-
- const mapToLine = (list: ThresholdUnitType[] | AvailabilityUnitType[], lineStyle: any) => {
- return list.map((thr: ThresholdUnitType) => {
- thresholdTraces.x.push(
- data[!isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name][0]
- );
- thresholdTraces.y.push(thr.value * (1 + 0.06));
- thresholdTraces.text.push(thr.name);
- return {
- type: 'line',
- x0: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0],
- y0: thr.value,
- x1: last(data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]),
- y1: thr.value,
- name: thr.name || '',
- opacity: 0.7,
- line: {
- color: thr.color,
- width: 3,
- ...lineStyle,
- },
- };
- });
- };
+ if (thresholds || availabilityConfig.level) {
+ const thresholdTraces = {
+ x: [],
+ y: [],
+ mode: 'text',
+ text: [],
+ showlegend: false,
+ };
- mergedLayout.shapes = [
- ...mapToLine(thresholds, { dash: 'dashdot' }),
- ...mapToLine(levels, {}),
- ];
- calculatedLineValues = [...calculatedLineValues, thresholdTraces];
- }
- return [mergedLayout, calculatedLineValues];
- }, [data, fields, lastIndex, layout, layoutConfig, xaxis, yaxis, mode, valueSeries]);
-
- const mergedConfigs = {
- ...config,
- ...(layoutConfig.config && layoutConfig.config),
- };
+ const levels = availabilityConfig.level ? availabilityConfig.level : [];
+
+ const mapToLine = (list: ThresholdUnitType[] | AvailabilityUnitType[], lineStyle: any) => {
+ return list.map((thr: ThresholdUnitType) => {
+ thresholdTraces.x.push(
+ queriedVizData[
+ !isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.name : fields[lastIndex].name
+ ][0]
+ );
+ thresholdTraces.y.push(thr.value * (1 + 0.06));
+ thresholdTraces.text.push(thr.name);
+ return {
+ type: 'line',
+ x0: queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.name : fields[lastIndex].name][0],
+ y0: thr.value,
+ x1: last(queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.name : fields[lastIndex].name]),
+ y1: thr.value,
+ name: thr.name || '',
+ opacity: 0.7,
+ line: {
+ color: thr.color,
+ width: 3,
+ ...lineStyle,
+ },
+ };
+ });
+ };
+
+ mergedLayout.shapes = [
+ ...mapToLine(
+ thresholds.filter((i: ThresholdUnitType) => i.value !== ''),
+ { dash: 'dashdot' }
+ ),
+ ...mapToLine(levels, {}),
+ ];
+
+ lines = [...lines, thresholdTraces];
+ }
+
+ const mergedConfigs = useMemo(
+ () => ({
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
+ }),
+ [config, layoutConfig.config]
+ );
- return ;
+ return ;
};
diff --git a/public/components/visualizations/charts/lines/line_type.ts b/public/components/visualizations/charts/lines/line_type.ts
index 20e452388..30be5bb2d 100644
--- a/public/components/visualizations/charts/lines/line_type.ts
+++ b/public/components/visualizations/charts/lines/line_type.ts
@@ -6,82 +6,211 @@
import { Line } from './line';
import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
import { LensIconChartLine } from '../../assets/chart_line';
-import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
import {
- ConfigValueOptions,
ConfigThresholds,
+ ConfigLineChartStyles,
+ ConfigLegend,
+ InputFieldItem,
+ ConfigColorTheme,
} 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 {
+ DEFAULT_CHART_STYLES,
+ VIS_CHART_TYPES,
+ PLOTLY_COLOR,
+} from '../../../../../common/constants/shared';
+import { ButtonGroupItem } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group';
+import { SliderConfig } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_style_slider';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const {
+ DefaultModeLine,
+ DefaultModeScatter,
+ Interpolation,
+ LineWidth,
+ FillOpacity,
+ MarkerSize,
+ LegendPosition,
+ ShowLegend,
+ LabelAngle,
+} = DEFAULT_CHART_STYLES;
export const createLineTypeDefinition = (params: any = {}) => ({
- name: 'line',
- type: 'line',
- id: 'line',
- label: 'Line',
- fullLabel: 'Line',
- iconType: 'visLine',
+ name: params.type,
+ type: params.type,
+ id: params.type,
+ label: params.type === VIS_CHART_TYPES.Line ? 'Time series' : 'Scatter',
+ fulllabel: params.type === VIS_CHART_TYPES.Line ? 'Time series' : 'Scatter',
+ icontype: 'visLine',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
icon: LensIconChartLine,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
- editorConfig: {
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
schemas: [
{
- name: 'X-axis',
- isSingleSelection: true,
+ name: 'Show legend',
+ mapTo: 'showLegend',
component: null,
- mapTo: 'xaxis',
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: ShowLegend }],
+ },
},
{
- name: 'Y-axis',
- isSingleSelection: false,
+ name: 'Position',
+ mapTo: 'position',
component: null,
- mapTo: 'yaxis',
+ props: {
+ options: [
+ { name: 'Right', id: 'v' },
+ { name: 'Bottom', id: 'h' },
+ ],
+ defaultSelections: [{ name: 'Right', id: LegendPosition }],
+ },
+ },
+ {
+ title: 'Legend size',
+ name: 'Legend size',
+ component: InputFieldItem,
+ mapTo: 'legendSize',
+ eleType: 'input',
},
],
},
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Dimension', id: 'x' },
+ { name: 'Series', id: 'y' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
{
- id: 'chart_options',
- name: 'Chart options',
- editor: ConfigValueOptions,
- mapTo: 'chartOptions',
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigLineChartStyles,
+ mapTo: 'chartStyles',
schemas: [
{
name: 'Mode',
- isSingleSelection: true,
- component: null,
- mapTo: 'mode',
+ component: ButtonGroupItem,
+ mapTo: 'style',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Lines', id: 'lines' },
+ { name: 'Marker', id: 'markers' },
+ { name: 'Lines + Markers', id: 'lines+markers' },
+ ],
+ defaultSelections: [
+ params.type === VIS_CHART_TYPES.Line
+ ? { name: 'Lines', id: DefaultModeLine }
+ : { name: 'Marker', id: DefaultModeScatter },
+ ],
+ },
+ },
+ {
+ name: 'Interpolation',
+ component: ButtonGroupItem,
+ mapTo: 'interpolation',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Linear', id: 'linear' },
+ { name: 'Smooth', id: 'spline' },
+ { name: 'Step before', id: 'hv' },
+ { name: 'Step after', id: 'vh' },
+ ],
+ defaultSelections: [{ name: 'Smooth', id: Interpolation }],
+ },
+ },
+ {
+ name: 'Line width',
+ component: SliderConfig,
+ mapTo: 'lineWidth',
+ defaultState: LineWidth,
+ eleType: 'slider',
props: {
- dropdownList: [
- { name: 'Markers', modeId: 'markers' },
- { name: 'Lines', modeId: 'lines' },
- { name: 'Lines + Markers', modeId: 'lines+markers' },
+ max: 10,
+ },
+ },
+ {
+ name: 'Fill opacity',
+ component: SliderConfig,
+ mapTo: 'fillOpacity',
+ defaultState: FillOpacity,
+ eleType: 'slider',
+ props: {
+ max: 100,
+ },
+ },
+ {
+ name: 'Point size',
+ component: SliderConfig,
+ mapTo: 'pointSize',
+ defaultState: MarkerSize,
+ eleType: 'slider',
+ props: {
+ max: 40,
+ },
+ },
+ {
+ title: 'Label size',
+ name: 'Label size',
+ component: InputFieldItem,
+ mapTo: 'labelSize',
+ eleType: 'input',
+ },
+ {
+ name: 'Rotate labels',
+ component: SliderConfig,
+ mapTo: 'rotateLabels',
+ eleType: 'slider',
+ defaultState: LabelAngle,
+ props: {
+ ticks: [
+ { label: '-90°', value: -90 },
+ { label: '-45°', value: -45 },
+ { label: '0°', value: 0 },
+ { label: '45°', value: 45 },
+ { label: '90°', value: 90 },
],
- defaultSelections: [{ name: 'Lines', modeId: 'lines' }],
+ showTicks: true,
+ min: -90,
+ max: 90,
},
},
],
},
+ {
+ id: 'color-theme',
+ name: 'Color theme',
+ editor: ConfigColorTheme,
+ mapTo: 'colorTheme',
+ schemas: [],
+ },
{
id: 'thresholds',
name: 'Thresholds',
@@ -93,11 +222,10 @@ export const createLineTypeDefinition = (params: any = {}) => ({
],
},
{
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
+ id: 'availability-panel',
+ name: 'Availability',
+ mapTo: 'availabilityConfig',
+ editor: ConfigAvailability,
},
{
id: 'availability-panel',
@@ -107,35 +235,31 @@ export const createLineTypeDefinition = (params: any = {}) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
- ...{
- colorway: PLOTLY_COLOR,
- plot_bgcolor: 'rgba(0, 0, 0, 0)',
- paper_bgcolor: 'rgba(0, 0, 0, 0)',
- xaxis: {
- fixedrange: true,
- showgrid: false,
- visible: true,
- },
- yaxis: {
- fixedrange: true,
- showgrid: false,
- visible: true,
- },
+ colorway: PLOTLY_COLOR,
+ plot_bgcolor: 'rgba(0, 0, 0, 0)',
+ paper_bgcolor: 'rgba(0, 0, 0, 0)',
+ xaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ yaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
},
},
config: {
...sharedConfigs.config,
- ...{
- barmode: 'line',
- xaxis: {
- automargin: true,
- },
- yaxis: {
- automargin: true,
- },
+ barmode: params.type,
+ xaxis: {
+ automargin: true,
+ },
+ yaxis: {
+ automargin: true,
},
},
},
diff --git a/public/components/visualizations/charts/logs_view/logs_view.scss b/public/components/visualizations/charts/logs_view/logs_view.scss
new file mode 100644
index 000000000..ae3ebebfb
--- /dev/null
+++ b/public/components/visualizations/charts/logs_view/logs_view.scss
@@ -0,0 +1,25 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+table {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+th,
+td {
+ text-align: left;
+}
+
+th {
+ width: 30%;
+}
+
+tr:hover {
+ background-color: #ddd;
+}
+
+.logs-view-container {
+ font-size: 16px;
+}
diff --git a/public/components/visualizations/charts/logs_view/logs_view.tsx b/public/components/visualizations/charts/logs_view/logs_view.tsx
new file mode 100644
index 000000000..0e8e88f4c
--- /dev/null
+++ b/public/components/visualizations/charts/logs_view/logs_view.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import React from 'react';
+import { IExplorerFields } from '../../../../../common/types/explorer';
+import { RAW_QUERY, SELECTED_TIMESTAMP } from '../../../../../common/constants/explorer';
+import { DataGrid } from '../../../../components/event_analytics/explorer/events_views/data_grid';
+import './logs_view.scss';
+
+export const LogsView = ({ visualizations }: any) => {
+ const explorer = visualizations?.data?.explorer;
+
+ const http = explorer?.http;
+ const pplService = explorer?.pplService;
+ const explorerData = explorer?.explorerData;
+ const explorerFields = explorer?.explorerFields;
+ const query = explorer?.query;
+
+ const emptyExplorerFields: IExplorerFields = {
+ availableFields: [],
+ queriedFields: explorerFields,
+ selectedFields: [],
+ unselectedFields: [],
+ };
+
+ return (
+
+
+
+ );
+};
diff --git a/public/components/visualizations/charts/logs_view/logs_view_type.ts b/public/components/visualizations/charts/logs_view/logs_view_type.ts
new file mode 100644
index 000000000..a4ecad4b8
--- /dev/null
+++ b/public/components/visualizations/charts/logs_view/logs_view_type.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { LogsView } from './logs_view';
+import { getPlotlyCategory, getPlotlySharedConfigs } from '../shared/shared_configs';
+import { LensIconChartDatatable } from '../../assets/chart_datatable';
+import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
+import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
+
+const sharedConfigs = getPlotlySharedConfigs();
+const VIS_CATEGORY = getPlotlyCategory();
+
+export const createLogsViewTypeDefinition = (params: any = {}) => ({
+ name: 'logs_view',
+ type: 'logs_view',
+ id: 'logs_view',
+ label: 'Logs view',
+ fulllabel: 'Logs view',
+ icontype: 'visTable',
+ category: VIS_CATEGORY.BASICS,
+ selection: {
+ dataLoss: 'nothing',
+ },
+ icon: LensIconChartDatatable,
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ editorconfig: {
+ panelTabs: [
+ {
+ id: 'data-panel',
+ name: 'Style',
+ mapTo: 'dataConfig',
+ editor: VizDataPanel,
+ sections: [],
+ },
+ ],
+ },
+ visconfig: {
+ layout: {
+ ...sharedConfigs.layout,
+ colorway: PLOTLY_COLOR,
+ plot_bgcolor: 'rgba(0, 0, 0, 0)',
+ paper_bgcolor: 'rgba(0, 0, 0, 0)',
+ xaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ yaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ },
+ config: {
+ ...sharedConfigs.config,
+ barmode: 'line',
+ xaxis: {
+ automargin: true,
+ },
+ yaxis: {
+ automargin: true,
+ },
+ },
+ },
+ component: LogsView,
+});
diff --git a/public/components/visualizations/charts/maps/heatmap.tsx b/public/components/visualizations/charts/maps/heatmap.tsx
index db746b50f..7bc51e497 100644
--- a/public/components/visualizations/charts/maps/heatmap.tsx
+++ b/public/components/visualizations/charts/maps/heatmap.tsx
@@ -2,53 +2,104 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
-
import React, { useMemo } from 'react';
-import { uniq, has, isArray, isEmpty } from 'lodash';
+import { colorPalette } from '@elastic/eui';
+import { has, isEmpty, uniq } from 'lodash';
import Plotly from 'plotly.js-dist';
-import { Plt } from '../../plotly/plot';
-import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
+import {
+ HEATMAP_PALETTE_COLOR,
+ HEATMAP_SINGLE_COLOR,
+ OPACITY,
+ SINGLE_COLOR_PALETTE,
+} from '../../../../../common/constants/colors';
+import {
+ hexToRgb,
+ lightenColor,
+ getPropName,
+} from '../../../../components/event_analytics/utils/utils';
+import { IVisualizationContainerProps } from '../../../../../common/types/explorer';
import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { Plt } from '../../plotly/plot';
+import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer';
+import { PLOT_MARGIN } from '../../../../../common/constants/shared';
export const HeatMap = ({ visualizations, layout, config }: any) => {
const {
- data,
- metadata: { fields },
- } = visualizations.data.rawVizData;
- const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs;
-
- if (fields.length < 3) return ;
-
- const xaxisField = fields[fields.length - 2];
- const yaxisField = fields[fields.length - 1];
- const zMetrics =
- dataConfig?.valueOptions && dataConfig?.valueOptions.zaxis
- ? dataConfig?.valueOptions.zaxis[0]
- : fields[fields.length - 3];
- const uniqueYaxis = uniq(data[yaxisField.name]);
- const uniqueXaxis = uniq(data[xaxisField.name]);
- const uniqueYaxisLength = uniqueYaxis.length;
- const uniqueXaxisLength = uniqueXaxis.length;
+ data: {
+ defaultAxes,
+ indexFields,
+ query,
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ chartStyles = {},
+ legend = {},
+ tooltipOptions = {},
+ panelOptions = {},
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ } = {},
+ layoutConfig = {},
+ } = {},
+ } = {},
+ vis: { icontype },
+ }: IVisualizationContainerProps = visualizations;
+
+ if (fields.length < 3) return ;
+
+ const xaxisField = dimensions[0];
+ const yaxisField = dimensions[1];
+ const zMetrics = series[0];
if (
isEmpty(xaxisField) ||
isEmpty(yaxisField) ||
isEmpty(zMetrics) ||
- isEmpty(data[xaxisField.name]) ||
- isEmpty(data[yaxisField.name]) ||
- isEmpty(data[zMetrics.name])
+ isEmpty(queriedVizData[xaxisField.label]) ||
+ isEmpty(queriedVizData[yaxisField.label]) ||
+ isEmpty(queriedVizData[getPropName(zMetrics)]) ||
+ dimensions.length > 2 ||
+ series.length > 1
)
- return ;
+ return ;
+
+ const uniqueYaxis = uniq(queriedVizData[yaxisField.label]);
+ const uniqueXaxis = uniq(queriedVizData[xaxisField.label]);
+ const uniqueYaxisLength = uniqueYaxis.length;
+ const uniqueXaxisLength = uniqueXaxis.length;
+ const tooltipMode =
+ tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show';
+ const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all';
+
+ const colorField = chartStyles
+ ? chartStyles.colorMode && chartStyles.colorMode[0].name === OPACITY
+ ? chartStyles.color ?? HEATMAP_SINGLE_COLOR
+ : chartStyles.scheme ?? HEATMAP_PALETTE_COLOR
+ : HEATMAP_PALETTE_COLOR;
+ const showColorscale = legend.showLegend ?? 'show';
- const colorScaleValues = [...PLOTLY_COLOR.map((clr, index) => [index, clr])];
+ const traceColor: any = [];
+ if (colorField.name === SINGLE_COLOR_PALETTE) {
+ const colorsArray = colorPalette([lightenColor(colorField.color, 50), colorField.color], 10);
+ colorsArray.map((hexCode, index) => {
+ traceColor.push([
+ (index !== colorsArray.length - 1 ? index : 10) / 10,
+ hexToRgb(hexCode, 1, false),
+ ]);
+ });
+ }
const calculatedHeapMapZaxis: Plotly.Data[] = useMemo(() => {
const heapMapZaxis = [];
const buckets = {};
// maps bukcets to metrics
- for (let i = 0; i < data[xaxisField.name].length; i++) {
- buckets[`${data[xaxisField.name][i]},${data[yaxisField.name][i]}`] = data[zMetrics.name][i];
+ for (let i = 0; i < queriedVizData[xaxisField.label].length; i++) {
+ buckets[`${queriedVizData[xaxisField.label][i]},${queriedVizData[yaxisField.label][i]}`] =
+ queriedVizData[getPropName(zMetrics)][i];
}
// initialize empty 2 dimensional array, inner loop for each xaxis field, outer loop for yaxis
@@ -71,7 +122,7 @@ export const HeatMap = ({ visualizations, layout, config }: any) => {
return heapMapZaxis;
}, [
- data,
+ queriedVizData,
uniqueYaxis,
uniqueXaxis,
uniqueYaxisLength,
@@ -86,21 +137,28 @@ export const HeatMap = ({ visualizations, layout, config }: any) => {
z: calculatedHeapMapZaxis,
x: uniqueXaxis,
y: uniqueYaxis,
- colorscale: colorScaleValues,
+ hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
+ colorscale: colorField.name === SINGLE_COLOR_PALETTE ? traceColor : colorField.name,
type: 'heatmap',
+ showscale: showColorscale === 'show',
},
];
+ layout.yaxis = { autosize: true, automargin: true };
const mergedLayout = {
...layout,
...(layoutConfig.layout && layoutConfig.layout),
- title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || zMetrics.name || '',
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ margin: PLOT_MARGIN,
};
- const mergedConfigs = {
- ...config,
- ...(layoutConfig.config && layoutConfig.config),
- };
+ const mergedConfigs = useMemo(
+ () => ({
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
+ }),
+ [config, layoutConfig.config]
+ );
return ;
};
diff --git a/public/components/visualizations/charts/maps/heatmap_type.ts b/public/components/visualizations/charts/maps/heatmap_type.ts
index 269e429c2..6501aa04a 100644
--- a/public/components/visualizations/charts/maps/heatmap_type.ts
+++ b/public/components/visualizations/charts/maps/heatmap_type.ts
@@ -8,7 +8,20 @@ import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_conf
import { LensIconChartPie } from '../../assets/chart_pie';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import {
+ ConfigValueOptions,
+ HeatmapColorPalettePicker,
+ ConfigChartOptions,
+ PanelItem,
+ SingleColorPicker,
+ ConfigLegend,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import {
+ COLOR_PALETTES,
+ HEATMAP_SINGLE_COLOR,
+ HEATMAP_PALETTE_COLOR,
+} from '../../../../../common/constants/colors';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
@@ -18,54 +31,98 @@ export const createMapsVisDefinition = () => ({
type: 'heatmap',
id: 'heatmap',
label: 'Heatmap',
- fullLabel: 'Hubble',
- iconType: 'heatmap',
+ fulllabel: 'Hubble',
+ icontype: 'heatmap',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
icon: LensIconChartPie,
- editorConfig: {
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Dim 1', id: 'x' },
+ { name: 'Dim 2', id: 'y' },
+ { name: 'Metrics', id: 'z' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
schemas: [
{
- name: 'Z-axis',
- isSingleSelection: true,
+ name: 'Show colorscale',
+ mapTo: 'showLegend',
component: null,
- mapTo: 'zaxis',
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: 'show' }],
+ },
+ },
+ ],
+ },
+ {
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
+ schemas: [
+ {
+ name: 'Color mode',
+ component: PanelItem,
+ mapTo: 'colorMode',
+ eleType: 'list',
+ isSingleSelection: true,
+ options: [
+ { name: 'spectrum', label: 'spectrum', value: 'spectrum' },
+ { name: 'opacity', label: 'opacity', value: 'opacity' },
+ ],
+ defaultState: [{ name: 'spectrum', label: 'spectrum', value: 'spectrum' }],
+ props: {
+ isClearable: false,
+ },
+ },
+ {
+ name: 'Scheme',
+ component: HeatmapColorPalettePicker,
+ mapTo: 'scheme',
+ eleType: 'palettePicker',
+ options: COLOR_PALETTES.filter((color) => color.type !== 'text'),
+ defaultState: HEATMAP_PALETTE_COLOR,
+ },
+ {
+ name: 'Color',
+ component: SingleColorPicker,
+ mapTo: 'color',
+ eleType: 'singleColorPicker',
+ defaultState: HEATMAP_SINGLE_COLOR,
},
],
},
],
},
- {
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
- },
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
- ...{
- plot_bgcolor: 'rgba(0, 0, 0, 0)',
- paper_bgcolor: 'rgba(0, 0, 0, 0)',
- margin: { left: 60 },
- },
+ plot_bgcolor: 'rgba(0, 0, 0, 0)',
+ paper_bgcolor: 'rgba(0, 0, 0, 0)',
+ margin: { left: 60 },
},
config: {
...sharedConfigs.config,
diff --git a/public/components/visualizations/charts/maps/treemap_type.ts b/public/components/visualizations/charts/maps/treemap_type.ts
index 74330f342..1a87a428b 100644
--- a/public/components/visualizations/charts/maps/treemap_type.ts
+++ b/public/components/visualizations/charts/maps/treemap_type.ts
@@ -8,53 +8,129 @@ import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_conf
import { LensIconChartBar } from '../../assets/chart_bar';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import {
+ ConfigValueOptions,
+ ColorPalettePicker,
+ ConfigChartOptions,
+ ConfigLegend,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { DEFAULT_PALETTE, COLOR_PALETTES } from '../../../../../common/constants/colors';
+import { ButtonGroupItem } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group';
+import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const { SortSectors } = DEFAULT_CHART_STYLES;
+
export interface BarTypeParams {}
export const createTreeMapDefinition = (params: BarTypeParams = {}) => ({
name: 'tree_map',
type: 'tree_map',
id: 'tree_map',
- label: 'Tree Map',
- fullLabel: 'Tree Map',
+ label: 'Tree map',
+ fulllabel: 'Tree map',
selection: {
dataLoss: 'nothing',
},
category: VIS_CATEGORY.BASICS,
+ icontype: 'heatmap',
icon: LensIconChartBar,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
orientation: 'v',
component: TreeMap,
- editorConfig: {
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Label', id: 'label' },
+ { name: 'Value', id: 'value' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
+ {
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
+ schemas: [
+ {
+ name: 'Show colorscale',
+ mapTo: 'showLegend',
+ component: null,
+ props: {
+ options: [
+ { name: 'Show', id: 'show' },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: 'show' }],
+ },
+ },
+ ],
+ },
{
- id: 'value_options',
- name: 'Value options',
+ id: 'treemap_options',
+ name: 'Treemap',
editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ mapTo: 'treemapOptions',
schemas: [
{
- name: 'X-axis',
+ name: 'Tiling algorithm',
isSingleSelection: true,
component: null,
- mapTo: 'xaxis',
+ mapTo: 'tilingAlgorithm',
+ options: [
+ { name: 'Squarify', value: 'squarify' },
+ { name: 'Binary', value: 'binary' },
+ { name: 'Dice', value: 'dice' },
+ { name: 'Slice', value: 'slice' },
+ { name: 'Slice Dice', value: 'slice-dice' },
+ { name: 'Dice Slice', value: 'dice-slice' },
+ ],
+ defaultState: [{ name: 'Squarify', label: 'Squarify', value: 'squarify' }],
+ props: {
+ isClearable: false,
+ },
},
{
- name: 'Y-axis',
- isSingleSelection: false,
- component: null,
- mapTo: 'yaxis',
+ name: 'Sort Sectors',
+ component: ButtonGroupItem,
+ mapTo: 'sort_sectors',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Largest to Smallest', id: 'largest_to_smallest' },
+ { name: 'Random', id: 'random' },
+ ],
+ defaultSelections: [{ name: 'Largest to Smallest', id: SortSectors }],
+ },
+ },
+ ],
+ },
+ {
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
+ schemas: [
+ {
+ name: 'Color theme',
+ isSingleSelection: true,
+ component: ColorPalettePicker,
+ mapTo: 'colorTheme',
+ eleType: 'treemapColorPicker',
+ options: COLOR_PALETTES,
+ defaultState: { name: DEFAULT_PALETTE },
},
],
},
@@ -69,7 +145,7 @@ export const createTreeMapDefinition = (params: BarTypeParams = {}) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
},
diff --git a/public/components/visualizations/charts/maps/treemaps.tsx b/public/components/visualizations/charts/maps/treemaps.tsx
index bdd014e64..e94bd5e50 100644
--- a/public/components/visualizations/charts/maps/treemaps.tsx
+++ b/public/components/visualizations/charts/maps/treemaps.tsx
@@ -3,57 +3,206 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
+import { isEmpty, isEqual, uniq } from 'lodash';
+import React, { useMemo } from 'react';
+import {
+ DEFAULT_PALETTE,
+ MULTI_COLOR_PALETTE,
+ SINGLE_COLOR_PALETTE,
+} from '../../../../../common/constants/colors';
+import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer';
+import { DEFAULT_CHART_STYLES, PLOT_MARGIN } from '../../../../../common/constants/shared';
+import { IVisualizationContainerProps } from '../../../../../common/types/explorer';
+import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
import { Plt } from '../../plotly/plot';
-export const TreeMap = ({ visualizations, layout, config }) => {
- const labels = ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'];
- const parents = ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve'];
- const treemapData = [
- {
- type: 'treemap',
- labels,
- parents,
- values: [10, 14, 12, 10, 2, 6, 6, 1, 4],
- textinfo: 'label+value+percent parent+percent entry',
- domain: { x: [0, 0.48] },
- outsidetextfont: { size: 20, color: '#377eb8' },
- marker: { line: { width: 2 } },
- pathbar: { visible: false },
- },
- {
- type: 'treemap',
- branchvalues: 'total',
- labels,
- parents,
- domain: { x: [0.52, 1] },
- values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
- textinfo: 'label+value+percent parent+percent entry',
- outsidetextfont: { size: 20, color: '#377eb8' },
- marker: { line: { width: 2 } },
- pathbar: { visible: false },
- },
- ];
- const finalLayout = {
- annotations: [
- {
- showarrow: false,
- text: 'branchvalues: remainder',
- x: 0.25,
- xanchor: 'center',
- y: 1.1,
- yanchor: 'bottom',
+export const TreeMap = ({ visualizations, layout, config }: any) => {
+ const { DefaultSortSectors } = DEFAULT_CHART_STYLES;
+ const {
+ data: {
+ defaultAxes,
+ indexFields,
+ query,
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
},
+ userConfigs: {
+ dataConfig: {
+ chartStyles = {},
+ legend = {},
+ tooltipOptions = {},
+ panelOptions = {},
+ treemapOptions = {},
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ },
+ layoutConfig = {},
+ },
+ },
+ vis: { icontype },
+ }: IVisualizationContainerProps = visualizations;
+
+ const childField =
+ dimensions && dimensions[0]?.childField ? dimensions[0]?.childField : fields[fields.length - 1];
+ const parentFields = dimensions && dimensions[0]?.parentFields ? dimensions[0]?.parentFields : [];
+ const tooltipMode =
+ tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show';
+ const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all';
+ const valueField = series && series[0]?.valueField ? series[0]?.valueField : fields[0];
+ const colorField =
+ chartStyles && chartStyles.colorTheme ? chartStyles.colorTheme : { name: DEFAULT_PALETTE };
+
+ const tilingAlgorithm =
+ treemapOptions && treemapOptions.tilingAlgorithm && !isEmpty(treemapOptions.tilingAlgorithm)
+ ? treemapOptions.tilingAlgorithm[0]
+ : 'squarify';
+
+ const sortSectorsField = treemapOptions.sort_sectors || DefaultSortSectors;
+ const showColorscale = legend.showLegend ?? 'show';
+
+ const areParentFieldsInvalid =
+ new Set([...parentFields.map((field) => field.name)]).size !== parentFields.length ||
+ parentFields.some(
+ (field) => isEmpty(queriedVizData[field.name]) || isEqual(childField.name, field.name)
+ );
+
+ if (
+ isEmpty(queriedVizData[childField.name]) ||
+ isEmpty(queriedVizData[valueField.name]) ||
+ areParentFieldsInvalid
+ )
+ return ;
+
+ const [treemapData, mergedLayout] = useMemo(() => {
+ let labelsArray: string[] = [];
+ let parentsArray: string[] = [];
+ let valuesArray: number[] = [];
+ let colorsArray: string[] = [];
+
+ if (parentFields.length === 0) {
+ labelsArray = [...queriedVizData[childField.name]];
+ parentsArray = [...Array(labelsArray.length).fill('')];
+ valuesArray = [...queriedVizData[valueField.name]];
+ if (colorField.name === MULTI_COLOR_PALETTE) {
+ colorsArray = [
+ ...Array(queriedVizData[childField.name].length).fill(colorField.childColor),
+ ];
+ }
+ } else {
+ let currentLevel = parentFields.length - 1;
+ let lastParentField = {};
+ parentFields
+ .slice(0)
+ .reverse()
+ .map((field, i) => {
+ const uniqueParents = uniq(queriedVizData[field.name]) as string[];
+ labelsArray = [...labelsArray, ...uniqueParents];
+ if (i === 0) {
+ parentsArray = [...Array(uniqueParents.length).fill('')];
+ valuesArray = [...Array(uniqueParents.length).fill(0)];
+ colorsArray =
+ colorField.name === MULTI_COLOR_PALETTE
+ ? [
+ ...Array(uniqueParents.length).fill(
+ colorField.parentColors[currentLevel] ?? '#000000'
+ ),
+ ]
+ : [];
+ } else {
+ const currentParentIndices = uniqueParents.map((parent) =>
+ queriedVizData[field.name].findIndex((index) => index === parent)
+ );
+ const lastParents = currentParentIndices.map(
+ (index) => queriedVizData[lastParentField.name][index]
+ );
+ parentsArray = [...parentsArray, ...lastParents];
+ valuesArray = [...valuesArray, ...Array(lastParents.length).fill(0)];
+ colorsArray =
+ colorField.name === MULTI_COLOR_PALETTE
+ ? [
+ ...colorsArray,
+ ...Array(lastParents.length).fill(
+ colorField.parentColors[currentLevel] ?? '#000000'
+ ),
+ ]
+ : [];
+ }
+ currentLevel = currentLevel - 1;
+ lastParentField = field;
+ });
+
+ labelsArray = [...labelsArray, ...queriedVizData[childField.name]];
+ valuesArray = [...valuesArray, ...queriedVizData[valueField.name]];
+ parentsArray = [...parentsArray, ...queriedVizData[lastParentField.name]];
+ colorsArray =
+ colorField.name === MULTI_COLOR_PALETTE
+ ? [
+ ...colorsArray,
+ ...Array(queriedVizData[childField.name].length).fill(colorField.childColor),
+ ]
+ : [];
+ }
+
+ if (colorField.name === SINGLE_COLOR_PALETTE) {
+ colorsArray = [...Array(valuesArray.length).fill(colorField.childColor)];
+ }
+
+ const markerColors =
+ colorField.name === MULTI_COLOR_PALETTE
+ ? { colors: colorsArray }
+ : ![DEFAULT_PALETTE, SINGLE_COLOR_PALETTE].includes(colorField.name)
+ ? {
+ colorscale: colorField.name,
+ colorbar: {
+ len: 1,
+ },
+ showscale: showColorscale === 'show',
+ }
+ : {};
+
+ const colorway = colorField.name === SINGLE_COLOR_PALETTE ? colorsArray : {};
+
+ const mapLayout = {
+ ...layout,
+ ...(layoutConfig.layout && layoutConfig.layout),
+ title: panelOptions.title || layoutConfig.layout?.title || '',
+ treemapcolorway: colorway,
+ margin: PLOT_MARGIN,
+ };
+
+ const mapData = [
{
- showarrow: false,
- text: 'branchvalues: total',
- x: 0.75,
- xanchor: 'center',
- y: 1.1,
- yanchor: 'bottom',
+ type: 'treemap',
+ labels: labelsArray,
+ parents: parentsArray,
+ values: valuesArray,
+ hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText,
+ textinfo: 'label+value+percent parent+percent entry',
+ tiling: {
+ packing: tilingAlgorithm.value,
+ },
+ marker: markerColors,
+ sort: sortSectorsField === DefaultSortSectors,
},
- ],
+ ];
+
+ return [mapData, mapLayout];
+ }, [
+ queriedVizData,
+ childField,
+ valueField,
+ parentFields,
+ colorField,
+ tilingAlgorithm,
+ layoutConfig,
+ ]);
+
+ const mergedConfigs = {
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
};
- return ;
+
+ return ;
};
diff --git a/public/components/visualizations/charts/metrics/metrics.tsx b/public/components/visualizations/charts/metrics/metrics.tsx
new file mode 100644
index 000000000..652769b9c
--- /dev/null
+++ b/public/components/visualizations/charts/metrics/metrics.tsx
@@ -0,0 +1,713 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { find, isEmpty, uniqBy } from 'lodash';
+import Plotly from 'plotly.js-dist';
+import React, { useMemo } from 'react';
+import { COLOR_BLACK, COLOR_WHITE } from '../../../../../common/constants/colors';
+import {
+ METRICS_GRID_SPACE_BETWEEN_X_AXIS,
+ METRICS_GRID_SPACE_BETWEEN_Y_AXIS,
+ DEFAULT_METRICS_CHART_PARAMETERS,
+ METRICS_AXIS_MARGIN,
+ METRICS_ANNOTATION,
+ METRICS_REDUCE_VALUE_SIZE_PERCENTAGE,
+ METRICS_REDUCE_TITLE_SIZE_PERCENTAGE,
+ METRICS_REDUCE_SERIES_UNIT_SIZE_PERCENTAGE,
+ METRICS_SERIES_UNIT_SUBSTRING_LENGTH,
+ GROUPBY,
+ AGGREGATIONS,
+} from '../../../../../common/constants/explorer';
+import {
+ DEFAULT_CHART_STYLES,
+ FILLOPACITY_DIV_FACTOR,
+} from '../../../../../common/constants/shared';
+import {
+ ConfigListEntry,
+ IVisualizationContainerProps,
+} from '../../../../../common/types/explorer';
+import { uiSettingsService } from '../../../../../common/utils';
+import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds';
+import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import {
+ getPropName,
+ getRoundOf,
+ getTooltipHoverInfo,
+ hexToRgb,
+} from '../../../event_analytics/utils/utils';
+import { Plt } from '../../plotly/plot';
+
+const {
+ DefaultOrientation,
+ DefaultTextMode,
+ DefaultChartType,
+ BaseThreshold,
+ DefaultTextColor,
+} = DEFAULT_METRICS_CHART_PARAMETERS;
+
+interface CreateAnnotationType {
+ index: number;
+ label: string;
+ value: number | string;
+ valueColor: string;
+}
+
+export const Metrics = ({ visualizations, layout, config }: any) => {
+ const {
+ data: {
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ span = {},
+ [GROUPBY]: xaxis = [],
+ [AGGREGATIONS]: series = [],
+ chartStyles = {},
+ panelOptions = {},
+ tooltipOptions = {},
+ thresholds = [],
+ },
+ layoutConfig = {},
+ },
+ },
+
+ vis: { charttype, titlesize, valuesize, textmode, orientation, precisionvalue },
+ }: IVisualizationContainerProps = visualizations;
+
+ // data config parametrs
+ const timestampField = find(fields, (field) => field.type === 'timestamp');
+
+ /**
+ * determine x axis
+ */
+ let xaxes: ConfigListEntry[];
+ if (span && span.time_field && timestampField) {
+ xaxes = [timestampField, ...xaxis];
+ } else {
+ xaxes = xaxis;
+ }
+
+ const seriesLength = series.length;
+ const chartType = chartStyles.chartType || charttype;
+
+ if (
+ isEmpty(queriedVizData) ||
+ (chartType === DefaultChartType && xaxes.length === 0) ||
+ seriesLength === 0
+ )
+ return ;
+
+ // thresholds
+ const appliedThresholds = thresholds.length ? thresholds : [BaseThreshold];
+ const sortedThresholds = uniqBy(
+ [...appliedThresholds].sort((a: ThresholdUnitType, b: ThresholdUnitType) => a.value - b.value),
+ 'value'
+ );
+
+ // style panel parameters
+ let titleSize =
+ chartStyles.titleSize ||
+ titlesize - titlesize * seriesLength * METRICS_REDUCE_TITLE_SIZE_PERCENTAGE;
+ const valueSize =
+ chartStyles.valueSize ||
+ valuesize - valuesize * seriesLength * METRICS_REDUCE_VALUE_SIZE_PERCENTAGE;
+ const selectedOrientation = chartStyles.orientation || orientation;
+ const chartOrientation =
+ selectedOrientation === DefaultOrientation || selectedOrientation === 'v'
+ ? DefaultOrientation
+ : 'h';
+ const selectedTextMode = chartStyles.textMode || textmode;
+ let textMode =
+ selectedTextMode === DefaultTextMode || selectedTextMode === 'values+names'
+ ? DefaultTextMode
+ : selectedTextMode;
+ const precisionValue = chartStyles.precisionValue || precisionvalue;
+ const seriesUnits =
+ chartStyles.seriesUnits?.substring(0, METRICS_SERIES_UNIT_SUBSTRING_LENGTH) || '';
+ const seriesUnitsSize = valueSize - valueSize * METRICS_REDUCE_SERIES_UNIT_SIZE_PERCENTAGE;
+ const isDarkMode = uiSettingsService.get('theme:darkMode');
+ const textColor = chartStyles.textColor?.childColor || DefaultTextColor;
+
+ if (chartType === 'text' && chartStyles.textMode === undefined) {
+ textMode = 'names';
+ titleSize = titlesize;
+ }
+
+ // margin from left of grid cell for label/value
+ const ANNOTATION_MARGIN_LEFT = seriesLength > 1 ? 0.01 : 0;
+ let autoChartLayout: object = {
+ annotations: [],
+ };
+
+ const xaxesData = xaxes.reduce((prev, cur) => {
+ if (queriedVizData[cur.name]) {
+ if (prev.length === 0) return queriedVizData[cur.name].flat();
+ return prev.map(
+ (item: string | number, index: number) => `${item},
${queriedVizData[cur.name][index]}`
+ );
+ }
+ }, []);
+
+ const createValueText = (value: string | number) =>
+ `${value}${
+ seriesUnits ? ` ${seriesUnits}` : ''
+ }`;
+
+ const calculateTextCooridinate = (seriesCount: number, index: number) => {
+ // calculating center of each subplot based on orienation vertical(single column) or horizontal(single row)
+ // splitting whole plot area with series length and find center of each individual subplot w.r.t index of series
+ if (seriesCount === 1) {
+ return 0.5;
+ } else if (index === 0) {
+ return 1 / seriesCount / 2;
+ }
+ return (index + 1) / seriesCount - 1 / seriesCount / 2;
+ };
+
+ const createAnnotationsAutoModeHorizontal = ({
+ label,
+ value,
+ index,
+ valueColor,
+ }: CreateAnnotationType) => {
+ const yCordinate = index > 0 ? (index + 1) / seriesLength : 1 / seriesLength;
+ return textMode === DefaultTextMode
+ ? [
+ {
+ ...METRICS_ANNOTATION,
+ x: ANNOTATION_MARGIN_LEFT,
+ y: yCordinate,
+ xanchor: 'left',
+ yanchor: 'top',
+ text: label,
+ font: {
+ size: titleSize,
+ color: isDarkMode ? COLOR_WHITE : COLOR_BLACK,
+ family: 'Roboto',
+ },
+ type: 'name',
+ seriesValue: value,
+ },
+ {
+ ...METRICS_ANNOTATION,
+ x: 1,
+ y: yCordinate,
+ xanchor: 'right',
+ yanchor: 'top',
+ text: createValueText(value),
+ font: {
+ size: valueSize,
+ color: valueColor,
+ family: 'Roboto',
+ },
+ type: 'value',
+ seriesValue: value,
+ },
+ ]
+ : [
+ {
+ ...METRICS_ANNOTATION,
+ x: 0.5,
+ y: calculateTextCooridinate(seriesLength, index),
+ xanchor: 'center',
+ yanchor: 'bottom',
+ text: textMode === 'values' ? createValueText(value) : label,
+ font: {
+ size: textMode === 'values' ? valueSize : titleSize,
+ color: textMode === 'names' ? (isDarkMode ? COLOR_WHITE : COLOR_BLACK) : valueColor,
+ family: 'Roboto',
+ },
+ type: textMode === 'names' ? 'name' : 'value',
+ seriesValue: value,
+ },
+ ];
+ };
+
+ const createAnnotationAutoModeVertical = ({
+ label,
+ value,
+ index,
+ valueColor,
+ }: CreateAnnotationType) => {
+ const xCoordinate = index / seriesLength + ANNOTATION_MARGIN_LEFT;
+ return textMode === DefaultTextMode
+ ? [
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'left',
+ yanchor: 'bottom',
+ text: label,
+ font: {
+ size: titleSize,
+ color: isDarkMode ? COLOR_WHITE : COLOR_BLACK,
+ family: 'Roboto',
+ },
+ x: xCoordinate,
+ y: 1,
+ seriesValue: value,
+ type: 'name',
+ },
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'left',
+ yanchor: 'top',
+ text: createValueText(value),
+ font: {
+ size: valueSize,
+ color: valueColor,
+ family: 'Roboto',
+ },
+ x: xCoordinate,
+ y: 1,
+ type: 'value',
+ seriesValue: value,
+ },
+ ]
+ : [
+ {
+ ...METRICS_ANNOTATION,
+ x: calculateTextCooridinate(seriesLength, index),
+ xanchor: 'center',
+ y: 0.95,
+ yanchor: 'bottom',
+ text: textMode === 'values' ? createValueText(value) : label,
+ font: {
+ size: textMode === 'values' ? valueSize : titleSize,
+ color: textMode === 'names' ? (isDarkMode ? COLOR_WHITE : COLOR_BLACK) : valueColor,
+ family: 'Roboto',
+ },
+ type: textMode === 'names' ? 'name' : 'value',
+ seriesValue: value,
+ },
+ ];
+ };
+
+ // extend y axis range to increase height of subplot w.r.t series data
+ const extendYaxisRange = (seriesLabel: string) => {
+ const sortedData = queriedVizData[seriesLabel]
+ .slice()
+ .sort((curr: number, next: number) => next - curr);
+ return isNaN(sortedData[0]) ? 100 : sortedData[0] + sortedData[0] / 2;
+ };
+
+ const getSeriesValue = (label: string) =>
+ typeof queriedVizData[label][queriedVizData[label].length - 1] === 'number'
+ ? getRoundOf(
+ queriedVizData[label][queriedVizData[label].length - 1],
+ Math.abs(precisionValue)
+ )
+ : 0;
+
+ const generateLineTraces = () => {
+ return series.map((seriesItem: ConfigListEntry, seriesIndex: number) => {
+ const seriesLabel = getPropName(seriesItem);
+ const isLabelExisted = queriedVizData[seriesLabel] ? true : false;
+ const annotationOption = {
+ label: seriesLabel,
+ value: isLabelExisted ? getSeriesValue(seriesLabel) : 0,
+ index: seriesIndex,
+ valueColor: '',
+ };
+ const layoutAxisIndex = seriesIndex > 0 ? seriesIndex + 1 : '';
+ autoChartLayout = {
+ ...autoChartLayout,
+ annotations: autoChartLayout.annotations.concat(
+ chartOrientation === DefaultOrientation || seriesLength === 1
+ ? createAnnotationAutoModeVertical(annotationOption)
+ : createAnnotationsAutoModeHorizontal(annotationOption)
+ ),
+ [`xaxis${layoutAxisIndex}`]: {
+ visible: false,
+ showgrid: false,
+ anchor: `y${layoutAxisIndex}`,
+ layoutFor: seriesLabel,
+ },
+ [`yaxis${layoutAxisIndex}`]: {
+ visible: false,
+ showgrid: false,
+ anchor: `x${layoutAxisIndex}`,
+ range: isLabelExisted ? [0, extendYaxisRange(seriesLabel)] : [0, 100],
+ layoutFor: seriesLabel,
+ },
+ };
+
+ return {
+ x: xaxesData,
+ y: queriedVizData[seriesLabel],
+ seriesValue: isLabelExisted ? getSeriesValue(seriesLabel) : 0,
+ fill: 'tozeroy',
+ mode: 'lines',
+ type: 'scatter',
+ fillcolor: '',
+ line: {
+ color: '',
+ },
+ name: seriesLabel,
+ ...(seriesIndex > 0 && {
+ xaxis: `x${seriesIndex + 1}`,
+ yaxis: `y${seriesIndex + 1}`,
+ }),
+ hoverinfo: getTooltipHoverInfo({
+ tooltipMode: tooltipOptions.tooltipMode,
+ tooltipText: tooltipOptions.tooltipText,
+ }),
+ };
+ });
+ };
+
+ const createAnnotationTextModeVertical = ({
+ label,
+ value,
+ index,
+ valueColor,
+ }: CreateAnnotationType) => {
+ return textMode === DefaultTextMode
+ ? [
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'left',
+ yanchor: seriesLength === 1 ? 'center' : 'bottom',
+ text: label,
+ font: {
+ size: titleSize,
+ color: textColor,
+ family: 'Roboto',
+ },
+ x:
+ seriesLength === 1
+ ? 0 + ANNOTATION_MARGIN_LEFT
+ : index / seriesLength + ANNOTATION_MARGIN_LEFT,
+ y: 0.5,
+ seriesValue: value,
+ type: 'name',
+ },
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: seriesLength === 1 ? 'right' : 'left',
+ yanchor: seriesLength === 1 ? 'center' : 'top',
+ text: createValueText(value),
+ font: {
+ size: valueSize,
+ color: textColor,
+ family: 'Roboto',
+ },
+ x:
+ seriesLength === 1
+ ? 1 - ANNOTATION_MARGIN_LEFT
+ : index / seriesLength + ANNOTATION_MARGIN_LEFT,
+ y: 0.5,
+ type: 'value',
+ seriesValue: value,
+ },
+ ]
+ : [
+ {
+ ...METRICS_ANNOTATION,
+ x: calculateTextCooridinate(seriesLength, index),
+ xanchor: 'center',
+ y: 0.5,
+ yanchor: 'center',
+ text: textMode === 'values' ? createValueText(value) : label,
+ font: {
+ size: textMode === 'values' ? valueSize : titleSize,
+ color: textColor,
+ family: 'Roboto',
+ },
+ type: textMode === 'names' ? 'name' : 'value',
+ seriesValue: value,
+ },
+ ];
+ };
+
+ const createAnnotationTextModeHorizontal = ({
+ label,
+ value,
+ index,
+ valueColor,
+ }: CreateAnnotationType) => {
+ return textMode === DefaultTextMode
+ ? [
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'left',
+ yanchor: 'center',
+ text: label,
+ font: {
+ size: titleSize,
+ color: COLOR_WHITE,
+ family: 'Roboto',
+ },
+ x: 0 + ANNOTATION_MARGIN_LEFT,
+ y: calculateTextCooridinate(seriesLength, index),
+ seriesValue: value,
+ type: 'name',
+ },
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'right',
+ yanchor: 'center',
+ text: createValueText(value),
+ font: {
+ size: valueSize,
+ color: COLOR_WHITE,
+ family: 'Roboto',
+ },
+ x: 1 - ANNOTATION_MARGIN_LEFT,
+ y: calculateTextCooridinate(seriesLength, index),
+ type: 'value',
+ seriesValue: value,
+ },
+ ]
+ : [
+ {
+ ...METRICS_ANNOTATION,
+ xanchor: 'center',
+ yanchor: 'center',
+ x: 0.5,
+ y: calculateTextCooridinate(seriesLength, index),
+ text: textMode === 'values' ? createValueText(value) : label,
+ font: {
+ size: textMode === 'values' ? valueSize : titleSize,
+ color: COLOR_WHITE,
+ family: 'Roboto',
+ },
+ type: textMode === 'names' ? 'name' : 'value',
+ seriesValue: value,
+ },
+ ];
+ };
+
+ const generateRectShapes = () => {
+ const shape = {
+ type: 'rect',
+ xsizemode: 'scaled',
+ layer: 'below',
+ yref: 'paper',
+ xref: 'paper',
+ line: {
+ color: '',
+ width: 3,
+ },
+ fillcolor: '',
+ };
+ const shapes: any = [];
+ series.forEach((seriesItem: ConfigListEntry, seriesIndex: number) => {
+ const seriesLabel = getPropName(seriesItem);
+ const isLabelExisted = queriedVizData[seriesLabel] ? true : false;
+ const seriesValue = isLabelExisted ? getSeriesValue(seriesLabel) : 0;
+ const axisIndex = seriesIndex > 0 ? seriesIndex + 1 : '';
+ const annotation = {
+ label: seriesLabel,
+ value: seriesValue,
+ index: seriesIndex,
+ valueColor: '',
+ };
+ autoChartLayout = {
+ ...autoChartLayout,
+ annotations: autoChartLayout.annotations.concat(
+ orientation === DefaultOrientation || seriesLength === 1
+ ? createAnnotationTextModeVertical({
+ ...annotation,
+ })
+ : createAnnotationTextModeHorizontal({
+ ...annotation,
+ })
+ ),
+ [`yaxis${axisIndex}`]: {
+ visible: false,
+ showgrid: false,
+ anchor: `x${axisIndex}`,
+ },
+ [`xaxis${axisIndex}`]: {
+ visible: false,
+ showgrid: false,
+ anchor: `y${axisIndex}`,
+ },
+ };
+
+ const nonSimilarAxis = orientation === DefaultOrientation ? 'x' : 'y';
+ const similarAxis = orientation === DefaultOrientation ? 'y' : 'x';
+ // for first metric
+ if (seriesIndex === 0) {
+ shapes.push({
+ ...shape,
+ [`${nonSimilarAxis}0`]: 0,
+ [`${nonSimilarAxis}1`]: 1 / seriesLength,
+ [`${similarAxis}0`]: 0,
+ [`${similarAxis}1`]: 1,
+ seriesValue,
+ });
+ } else {
+ shapes.push({
+ ...shape,
+ [`${nonSimilarAxis}0`]:
+ shapes[shapes.length - 1][`${nonSimilarAxis}1`] + METRICS_GRID_SPACE_BETWEEN_X_AXIS,
+ [`${nonSimilarAxis}1`]:
+ shapes[shapes.length - 1][`${nonSimilarAxis}1`] + 1 / seriesLength,
+ [`${similarAxis}0`]: 0,
+ [`${similarAxis}1`]: 1,
+ seriesValue,
+ });
+ }
+ });
+ return shapes;
+ };
+
+ const [statsData, statsLayout]: Plotly.Data[] = useMemo(() => {
+ let calculatedStatsData: Plotly.Data[] = [];
+ let sortedStatsData: Plotly.Data[] = [];
+ let sortedShapesData = [];
+ if (chartType === DefaultChartType) {
+ calculatedStatsData = generateLineTraces();
+ sortedStatsData = calculatedStatsData
+ .map((stat, statIndex) => ({ ...stat, oldIndex: statIndex }))
+ .sort((statCurrent, statNext) => statCurrent.seriesValue - statNext.seriesValue);
+ } else {
+ const shapes = generateRectShapes();
+ autoChartLayout = {
+ ...autoChartLayout,
+ shapes,
+ };
+ sortedShapesData = shapes
+ .map((shape: object, shapeIndex: number) => ({ ...shape, oldIndex: shapeIndex }))
+ .sort((current: object, next: object) => current.seriesValue - next.seriesValue);
+ }
+
+ if (sortedThresholds.length) {
+ // threshold ranges with min, max values
+ let thresholdRanges: number[][] = [];
+ const maxValue =
+ chartType === DefaultChartType
+ ? sortedStatsData[sortedStatsData.length - 1].seriesValue
+ : sortedShapesData[sortedShapesData.length - 1].seriesValue;
+ thresholdRanges = sortedThresholds.map((thresh, index) => [
+ thresh.value,
+ index === sortedThresholds.length - 1 ? maxValue : sortedThresholds[index + 1].value,
+ ]);
+
+ if (chartType === DefaultChartType) {
+ if (thresholdRanges.length) {
+ // change color for line traces
+ for (let statIndex = 0; statIndex < sortedStatsData.length; statIndex++) {
+ for (let threshIndex = 0; threshIndex < thresholdRanges.length; threshIndex++) {
+ if (
+ Number(sortedStatsData[statIndex].seriesValue) >=
+ Number(thresholdRanges[threshIndex][0]) &&
+ Number(sortedStatsData[statIndex].seriesValue) <=
+ Number(thresholdRanges[threshIndex][1])
+ ) {
+ calculatedStatsData[sortedStatsData[statIndex].oldIndex].fillcolor = hexToRgb(
+ sortedThresholds[threshIndex].color,
+ DEFAULT_CHART_STYLES.FillOpacity / FILLOPACITY_DIV_FACTOR
+ );
+ calculatedStatsData[sortedStatsData[statIndex].oldIndex].line.color =
+ sortedThresholds[threshIndex].color;
+ }
+ }
+ }
+
+ // change color of text annotations
+ for (
+ let annotationIndex = 0;
+ annotationIndex < autoChartLayout.annotations.length;
+ annotationIndex++
+ ) {
+ const isSeriesValueText = autoChartLayout.annotations[annotationIndex].type === 'value';
+ const seriesValue = Number(autoChartLayout.annotations[annotationIndex].seriesValue);
+ for (let threshIndex = 0; threshIndex < thresholdRanges.length; threshIndex++) {
+ if (
+ isSeriesValueText &&
+ seriesValue >= Number(thresholdRanges[threshIndex][0]) &&
+ seriesValue <= Number(thresholdRanges[threshIndex][1])
+ ) {
+ autoChartLayout.annotations[annotationIndex].font.color =
+ sortedThresholds[threshIndex].color;
+ }
+ }
+ }
+ }
+ } else {
+ // change color of shapes
+ for (let shapeIndex = 0; shapeIndex < sortedShapesData.length; shapeIndex++) {
+ for (let threshIndex = 0; threshIndex < thresholdRanges.length; threshIndex++) {
+ const seriesValue = Number(sortedShapesData[shapeIndex].seriesValue);
+ if (
+ seriesValue >= Number(thresholdRanges[threshIndex][0]) &&
+ seriesValue <= Number(thresholdRanges[threshIndex][1])
+ ) {
+ const color = sortedThresholds[threshIndex].color;
+ autoChartLayout.shapes[sortedShapesData[shapeIndex].oldIndex].fillcolor = color;
+ autoChartLayout.shapes[sortedShapesData[shapeIndex].oldIndex].line = {
+ ...autoChartLayout.shapes[sortedShapesData[shapeIndex].oldIndex].line,
+ color,
+ };
+ }
+ }
+ }
+ }
+ }
+ return [chartType === DefaultChartType ? calculatedStatsData : [], autoChartLayout];
+ }, [
+ xaxes,
+ series,
+ fields,
+ appliedThresholds,
+ chartOrientation,
+ titleSize,
+ valueSize,
+ textMode,
+ seriesUnits,
+ queriedVizData,
+ precisionValue,
+ ]);
+
+ const mergedLayout = useMemo(() => {
+ return {
+ ...layout,
+ ...(layoutConfig.layout && layoutConfig.layout),
+ showlegend: false,
+ margin:
+ chartType === DefaultChartType
+ ? METRICS_AXIS_MARGIN
+ : panelOptions.title || layoutConfig.layout?.title
+ ? METRICS_AXIS_MARGIN
+ : { ...METRICS_AXIS_MARGIN, t: 0 },
+ ...statsLayout,
+ grid: {
+ ...(chartOrientation === DefaultOrientation
+ ? {
+ rows: 1,
+ columns: seriesLength,
+ xgap: METRICS_GRID_SPACE_BETWEEN_X_AXIS,
+ }
+ : {
+ rows: seriesLength,
+ columns: 1,
+ ygap: METRICS_GRID_SPACE_BETWEEN_Y_AXIS,
+ }),
+ pattern: 'independent',
+ roworder: 'bottom to top',
+ },
+ title: panelOptions?.title || layoutConfig.layout?.title || '',
+ };
+ }, [
+ chartType,
+ layout,
+ layoutConfig.layout,
+ panelOptions?.title,
+ orientation,
+ seriesLength,
+ statsLayout,
+ ]);
+
+ const mergedConfigs = {
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
+ };
+
+ return ;
+};
diff --git a/public/components/visualizations/charts/metrics/metrics_type.ts b/public/components/visualizations/charts/metrics/metrics_type.ts
new file mode 100644
index 000000000..11b835def
--- /dev/null
+++ b/public/components/visualizations/charts/metrics/metrics_type.ts
@@ -0,0 +1,197 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Metrics } from './metrics';
+import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
+import { LensIconChartLine } from '../../assets/chart_line';
+import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
+import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
+import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
+import {
+ ConfigThresholds,
+ InputFieldItem,
+ ButtonGroupItem,
+ ConfigChartOptions,
+ TextInputFieldItem,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { fetchConfigObject } from '../../../event_analytics/utils/utils';
+import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability';
+import { DEFAULT_METRICS_CHART_PARAMETERS } from '../../../../../common/constants/explorer';
+
+const sharedConfigs = getPlotlySharedConfigs();
+const VIS_CATEGORY = getPlotlyCategory();
+const {
+ DefaultTextMode,
+ DefaultOrientation,
+ DefaultChartType,
+ DefaultPrecision,
+ DefaultTitleSize,
+ DefaultValueSize,
+ BaseThreshold,
+} = DEFAULT_METRICS_CHART_PARAMETERS;
+
+export const createMetricsTypeDefinition = (params: any = {}) => ({
+ name: 'metrics',
+ type: 'metrics',
+ id: 'metrics',
+ label: 'Metrics',
+ fulllabel: 'Metrics',
+ icontype: 'stats',
+ category: VIS_CATEGORY.BASICS,
+ icon: LensIconChartLine,
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ charttype: DefaultChartType,
+ precisionvalue: DefaultPrecision,
+ titlesize: DefaultTitleSize,
+ valuesize: DefaultValueSize,
+ textmode: DefaultTextMode,
+ orientation: DefaultOrientation,
+ editorconfig: {
+ panelTabs: [
+ {
+ id: 'data-panel',
+ name: 'Style',
+ mapTo: 'dataConfig',
+ editor: VizDataPanel,
+ sections: [
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Dimension', id: 'x' },
+ { name: 'Series', id: 'y' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
+ {
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
+ schemas: [
+ {
+ name: 'Chart type',
+ mapTo: 'chartType',
+ component: ButtonGroupItem,
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Auto', id: DefaultChartType },
+ { name: 'Horizontal', id: 'horizontal' },
+ { name: 'Text mode', id: 'text' },
+ ],
+ defaultSelections: [{ name: 'Auto', id: DefaultChartType }],
+ },
+ },
+ {
+ name: 'Orientation',
+ component: ButtonGroupItem,
+ mapTo: 'orientation',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Auto', id: DefaultOrientation },
+ { name: 'Horizontal', id: 'h' },
+ { name: 'Vertical', id: 'v' },
+ ],
+ defaultSelections: [{ name: 'Auto', id: DefaultOrientation }],
+ },
+ },
+ {
+ title: 'Series units',
+ name: 'Series units',
+ component: TextInputFieldItem,
+ mapTo: 'seriesUnits',
+ eleType: 'textInput',
+ },
+ {
+ title: 'Series precision',
+ name: 'Series precision',
+ component: InputFieldItem,
+ mapTo: 'precisionValue',
+ eleType: 'input',
+ props: {
+ minLimit: 0,
+ },
+ },
+ {
+ title: 'Title size',
+ name: 'Title size',
+ component: InputFieldItem,
+ mapTo: 'titleSize',
+ eleType: 'input',
+ },
+ {
+ title: 'Value size',
+ name: 'Value size',
+ component: InputFieldItem,
+ mapTo: 'valueSize',
+ eleType: 'input',
+ },
+ {
+ name: 'Text mode',
+ component: ButtonGroupItem,
+ mapTo: 'textMode',
+ eleType: 'buttons',
+ props: {
+ options: [
+ { name: 'Auto', id: DefaultTextMode },
+ { name: 'Names', id: 'names' },
+ { name: 'Values', id: 'values' },
+ { name: 'Values + Names', id: 'values+names' },
+ ],
+ defaultSelections: [{ name: 'Values + Names', id: DefaultTextMode }],
+ },
+ },
+ ],
+ },
+ {
+ id: 'thresholds',
+ name: 'Thresholds',
+ editor: ConfigThresholds,
+ mapTo: 'thresholds',
+ defaultState: [BaseThreshold],
+ schemas: [],
+ },
+ ],
+ },
+ {
+ id: 'style-panel',
+ name: 'Layout',
+ mapTo: 'layoutConfig',
+ editor: ConfigEditor,
+ content: [],
+ },
+ {
+ id: 'availability-panel',
+ name: 'Availability',
+ mapTo: 'availabilityConfig',
+ editor: ConfigAvailability,
+ },
+ ],
+ },
+ visconfig: {
+ layout: {
+ ...sharedConfigs.layout,
+ colorway: PLOTLY_COLOR,
+ plot_bgcolor: 'rgba(0, 0, 0, 0)',
+ paper_bgcolor: 'rgba(0, 0, 0, 0)',
+ xaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ yaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ },
+ config: {
+ ...sharedConfigs.config,
+ },
+ },
+ component: Metrics,
+});
diff --git a/public/components/visualizations/charts/pie/pie.tsx b/public/components/visualizations/charts/pie/pie.tsx
index 0c07892e9..48cf5c366 100644
--- a/public/components/visualizations/charts/pie/pie.tsx
+++ b/public/components/visualizations/charts/pie/pie.tsx
@@ -3,66 +3,174 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
-import { take, isEmpty } from 'lodash';
+import { find, isEmpty } from 'lodash';
+import React, { useMemo } from 'react';
+import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors';
+import {
+ AGGREGATIONS,
+ GROUPBY,
+ PIE_XAXIS_GAP,
+ PIE_YAXIS_GAP,
+ PLOTLY_PIE_COLUMN_NUMBER,
+} from '../../../../../common/constants/explorer';
+import { PLOT_MARGIN } from '../../../../../common/constants/shared';
+import {
+ ConfigListEntry,
+ IVisualizationContainerProps,
+} from '../../../../../common/types/explorer';
+import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { getPropName, getTooltipHoverInfo } from '../../../event_analytics/utils/utils';
import { Plt } from '../../plotly/plot';
export const Pie = ({ visualizations, layout, config }: any) => {
- const { vis } = visualizations;
const {
- data,
- metadata: { fields },
- } = visualizations.data.rawVizData;
- const { defaultAxes } = visualizations.data;
- const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs;
- const xaxis =
- dataConfig?.valueOptions && dataConfig.valueOptions.xaxis ? dataConfig.valueOptions.xaxis : [];
- const yaxis =
- dataConfig?.valueOptions && dataConfig.valueOptions.yaxis ? dataConfig.valueOptions.yaxis : [];
- const type = dataConfig?.chartOptions?.mode ? dataConfig?.chartOptions?.mode[0]?.modeId : 'pie';
- const lastIndex = fields.length - 1;
+ data: {
+ defaultAxes,
+ indexFields,
+ query,
+ rawVizData: {
+ data: queriedVizData,
+ metadata: { fields },
+ },
+ userConfigs: {
+ dataConfig: {
+ chartStyles = {},
+ span = {},
+ legend = {},
+ panelOptions = {},
+ tooltipOptions = {},
+ [GROUPBY]: dimensions = [],
+ [AGGREGATIONS]: series = [],
+ } = {},
+ layoutConfig = {},
+ } = {},
+ } = {},
+ vis: { mode, icontype, showlegend, legendSize, labelSize, legendposition },
+ }: IVisualizationContainerProps = visualizations;
+
+ const type = chartStyles.mode || mode;
+ const colorTheme = chartStyles.colorTheme ? chartStyles.colorTheme : { name: DEFAULT_PALETTE };
+ const showLegend = legend.showLegend === 'hidden' ? false : showlegend;
+ const chartLegendSize = legend.size || legendSize;
+ const chartLabelSize = chartStyles.labelSize || labelSize;
+ const title = panelOptions.title || layoutConfig.layout?.title || '';
+ const timestampField = find(fields, (field) => field.type === 'timestamp');
- let valueSeries;
- if (!isEmpty(xaxis) && !isEmpty(yaxis)) {
- valueSeries = [...yaxis];
+ /**
+ * determine x axis
+ */
+ let xaxes: ConfigListEntry[] = [];
+ if (span && span.time_field && timestampField) {
+ xaxes = [timestampField, ...dimensions];
} else {
- valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1);
+ xaxes = dimensions;
+ }
+
+ if (isEmpty(xaxes) || isEmpty(series)) {
+ return ;
}
- const pies = valueSeries.map((field: any, index) => {
+ const invertHex = (hex: string) =>
+ (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase();
+
+ const labelsOfXAxis = xaxes.reduce((prev, cur) => {
+ if (queriedVizData[cur.name]) {
+ if (prev.length === 0) return queriedVizData[cur.name].flat();
+ return prev.map(
+ (item: string | number, index: number) => `${item}, ${queriedVizData[cur.name][index]}`
+ );
+ }
+ }, []);
+
+ const hexColor = invertHex(colorTheme);
+
+ const pies = useMemo(
+ () =>
+ series.map((field: any, index: number) => {
+ const fieldName = getPropName(field);
+ const marker =
+ colorTheme.name !== DEFAULT_PALETTE
+ ? {
+ marker: {
+ colors: [...Array(queriedVizData[fieldName].length).fill(colorTheme.childColor)],
+ line: {
+ color: hexColor,
+ width: 1,
+ },
+ },
+ }
+ : undefined;
+ return {
+ labels: labelsOfXAxis,
+ values: queriedVizData[fieldName],
+ type: 'pie',
+ name: getPropName(field),
+ hole: type === 'pie' ? 0 : 0.5,
+ text: fieldName,
+ textinfo: 'percent',
+ hoverinfo: getTooltipHoverInfo({
+ tooltipMode: tooltipOptions.tooltipMode,
+ tooltipText: tooltipOptions.tooltipText,
+ }),
+ automargin: true,
+ textposition: 'outside',
+ title: { text: fieldName },
+ domain: {
+ row: Math.floor(index / PLOTLY_PIE_COLUMN_NUMBER),
+ column: index % PLOTLY_PIE_COLUMN_NUMBER,
+ },
+ ...marker,
+ outsidetextfont: {
+ size: chartLabelSize,
+ },
+ };
+ }),
+ [series, queriedVizData, chartLabelSize, labelsOfXAxis, colorTheme]
+ );
+
+ const mergedLayout = useMemo(() => {
+ const isAtleastOneFullRow = Math.floor(series.length / PLOTLY_PIE_COLUMN_NUMBER) > 0;
return {
- labels: data[xaxis ? xaxis[0]?.label : fields[lastIndex].name],
- values: data[field.name],
- type: 'pie',
- name: field.name,
- hole: type === 'pie' ? 0 : 0.5,
- text: field.name,
- textinfo: 'percent',
- automargin: true,
- textposition: 'outside',
- domain: {
- row: Math.floor(index / 3),
- column: index % 3,
+ grid: {
+ xgap: PIE_XAXIS_GAP,
+ ygap: PIE_YAXIS_GAP,
+ rows: Math.floor(series.length / PLOTLY_PIE_COLUMN_NUMBER) + 1,
+ columns: isAtleastOneFullRow ? PLOTLY_PIE_COLUMN_NUMBER : series.length,
+ pattern: 'independent',
+ },
+ ...layout,
+ ...(layoutConfig.layout && layoutConfig.layout),
+ legend: {
+ ...layout.legend,
+ orientation: legend.position || legendposition,
+ ...(chartLegendSize && {
+ font: { size: chartLegendSize },
+ }),
+ },
+ showlegend: showLegend,
+ margin: {
+ ...PLOT_MARGIN,
+ t: 100,
+ },
+ title: {
+ text: title,
+ xanchor: 'right',
+ yanchor: 'top',
+ x: 1,
+ y: 1,
+ xref: 'paper',
+ yref: 'container',
},
};
- });
-
- const isAtleastOneFullRow = Math.floor(valueSeries.length / 3) > 0;
-
- const mergedLayout = {
- grid: {
- rows: Math.floor(valueSeries.length / 3) + 1,
- columns: isAtleastOneFullRow ? 3 : valueSeries.length,
- },
- ...layout,
- ...(layoutConfig.layout && layoutConfig.layout),
- title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '',
- };
+ }, [series, layoutConfig.layout, title, layout.legend]);
- const mergedConfigs = {
- ...config,
- ...(layoutConfig.config && layoutConfig.config),
- };
+ const mergedConfigs = useMemo(
+ () => ({
+ ...config,
+ ...(layoutConfig.config && layoutConfig.config),
+ }),
+ [config, layoutConfig.config]
+ );
return ;
};
diff --git a/public/components/visualizations/charts/pie/pie_type.ts b/public/components/visualizations/charts/pie/pie_type.ts
index 7dbc507aa..690754556 100644
--- a/public/components/visualizations/charts/pie/pie_type.ts
+++ b/public/components/visualizations/charts/pie/pie_type.ts
@@ -6,107 +6,151 @@
import { Pie } from './pie';
import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs';
import { LensIconChartPie } from '../../assets/chart_pie';
-import { PLOTLY_COLOR } from '../../../../../common/constants/shared';
import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor';
import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor';
-import { ConfigValueOptions } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import {
+ ColorPalettePicker,
+ ConfigChartOptions,
+ ConfigLegend,
+ InputFieldItem,
+ ButtonGroupItem,
+} from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls';
+import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils';
+import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors';
+import { PLOTLY_COLOR, DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared';
+import { DEFAULT_PIE_CHART_PARAMETERS } from '../../../../../common/constants/explorer';
const sharedConfigs = getPlotlySharedConfigs();
const VIS_CATEGORY = getPlotlyCategory();
+const { ShowLegend, LegendPosition } = DEFAULT_CHART_STYLES;
+const { DefaultMode } = DEFAULT_PIE_CHART_PARAMETERS;
+
export const createPieTypeDefinition = (params: any) => ({
name: 'pie',
type: 'pie',
id: 'pie',
label: 'Pie',
- fullLabel: 'Pie',
- iconType: 'visPie',
+ fulllabel: 'Pie',
+ icontype: 'visPie',
category: VIS_CATEGORY.BASICS,
- selection: {
- dataLoss: 'nothing',
- },
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
+ showlegend: ShowLegend,
+ legendposition: LegendPosition,
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ mode: DefaultMode,
icon: LensIconChartPie,
- editorConfig: {
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
{
- id: 'value_options',
- name: 'Value options',
- editor: ConfigValueOptions,
- mapTo: 'valueOptions',
+ id: 'legend',
+ name: 'Legend',
+ editor: ConfigLegend,
+ mapTo: 'legend',
schemas: [
{
- name: 'Label',
- onChangeHandler: 'setXaxisSelections',
- isSingleSelection: false,
+ name: 'Show legend',
+ mapTo: 'showLegend',
component: null,
- mapTo: 'xaxis',
+ props: {
+ options: [
+ { name: 'Show', id: ShowLegend },
+ { name: 'Hidden', id: 'hidden' },
+ ],
+ defaultSelections: [{ name: 'Show', id: ShowLegend }],
+ },
},
{
- name: 'Value',
- onChangeHandler: 'setYaxisSelections',
- isSingleSelection: false,
+ name: 'Position',
+ mapTo: 'position',
component: null,
- mapTo: 'yaxis',
+ props: {
+ options: [
+ { name: 'Right', id: LegendPosition },
+ { name: 'Bottom', id: 'h' },
+ ],
+ defaultSelections: [{ name: 'Right', id: LegendPosition }],
+ },
+ },
+ {
+ name: 'Legend size',
+ component: InputFieldItem,
+ mapTo: 'size',
+ eleType: 'input',
},
],
},
+ fetchConfigObject('Tooltip', {
+ options: [
+ { name: 'All', id: 'all' },
+ { name: 'Label', id: 'label' },
+ { name: 'Value', id: 'value' },
+ { name: 'Percent', id: 'percent' },
+ ],
+ defaultSelections: [{ name: 'All', id: 'all' }],
+ }),
{
- id: 'chart_options',
- name: 'Chart options',
- editor: ConfigValueOptions,
- mapTo: 'chartOptions',
+ id: 'chart_styles',
+ name: 'Chart styles',
+ editor: ConfigChartOptions,
+ mapTo: 'chartStyles',
schemas: [
{
name: 'Mode',
isSingleSelection: true,
- component: null,
+ component: ButtonGroupItem,
+ eleType: 'buttons',
mapTo: 'mode',
props: {
- dropdownList: [
- { name: 'Pie', modeId: 'pie' },
- { name: 'Donut', modeId: 'donut' },
+ options: [
+ { name: 'Pie', id: DefaultMode },
+ { name: 'Donut', id: 'donut' },
],
- defaultSelections: [{ name: 'Pie', modeId: 'pie' }],
+ defaultSelections: [{ name: 'Pie', id: DefaultMode }],
},
},
+ {
+ name: 'Label size',
+ component: InputFieldItem,
+ mapTo: 'labelSize',
+ eleType: 'input',
+ },
+ {
+ name: 'Color theme',
+ isSingleSelection: true,
+ component: ColorPalettePicker,
+ mapTo: 'colorTheme',
+ eleType: 'colorpicker',
+ options: PIE_PALETTES,
+ defaultState: { name: DEFAULT_PALETTE },
+ },
],
},
],
},
- {
- id: 'style-panel',
- name: 'Layout',
- mapTo: 'layoutConfig',
- editor: ConfigEditor,
- content: [],
- },
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
- ...{
- colorway: PLOTLY_COLOR,
- plot_bgcolor: 'rgba(0, 0, 0, 0)',
- paper_bgcolor: 'rgba(0, 0, 0, 0)',
- xaxis: {
- fixedrange: true,
- showgrid: false,
- visible: true,
- },
- yaxis: {
- fixedrange: true,
- showgrid: false,
- visible: true,
- },
+ colorway: PLOTLY_COLOR,
+ plot_bgcolor: 'rgba(0, 0, 0, 0)',
+ paper_bgcolor: 'rgba(0, 0, 0, 0)',
+ xaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
+ },
+ yaxis: {
+ fixedrange: true,
+ showgrid: false,
+ visible: true,
},
},
config: {
diff --git a/public/components/visualizations/charts/shared/common.ts b/public/components/visualizations/charts/shared/common.ts
new file mode 100644
index 000000000..e5c0b06d4
--- /dev/null
+++ b/public/components/visualizations/charts/shared/common.ts
@@ -0,0 +1,92 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { isEmpty, forEach } from 'lodash';
+import { CUSTOM_LABEL } from '../../../../../common/constants/explorer';
+import { ConfigList, DimensionSpan } from '../../../../../common/types/explorer';
+
+export const getCompleteTimespanKey = (span: DimensionSpan) => {
+ if (!span || isEmpty(span.time_field) || isEmpty(span.interval) || isEmpty(span.unit)) return '';
+ return { name: `span(${span.time_field[0]?.name},${span.interval}${span.unit[0]?.value})` };
+};
+
+/**
+ * Transform to traces that can be consumed by plotly.
+ * @param intermediateVisData preprocessed json data that has dimensions to single aggregation mapping.
+ * @param param1 required visualization configurations.
+ * @returns traces.
+ */
+export const transformPreprocessedDataToTraces = (
+ intermediateVisData: Array,
+ { breakdowns, isVertical = true }: Partial
+) => {
+ const traceMap = new Map();
+ const hasBreakdown = !isEmpty(breakdowns);
+ forEach(intermediateVisData, (entry) => {
+ const traceKey = hasBreakdown ? [entry.breakdown, entry.aggName].join(',') : entry.aggName;
+ if (isEmpty(traceMap.get(traceKey))) {
+ traceMap.set(traceKey, {
+ x: isVertical ? [entry.x] : [entry.value],
+ y: isVertical ? [entry.value] : [entry.x],
+ name: hasBreakdown ? [entry.breakdown, entry.aggName].join(',') : `${traceKey}`,
+ });
+ } else {
+ const curTrace = traceMap.get(traceKey);
+ const xaxisValue = isVertical ? entry.x : entry.value;
+ const yaxisValue = isVertical ? entry.value : entry.x;
+ curTrace!.x.push(xaxisValue);
+ curTrace!.y.push(yaxisValue);
+ }
+ });
+ return [...traceMap.values()];
+};
+
+/**
+ * preprocess json data to
+ * 1. concatenate dimensions to generate one dimension
+ * 2. concatenate breakdowns (if there's any) generate one breakdown
+ * 3. map dimension/breakdown to aggregations
+ * @param visJson raw json data from data fetching
+ * @param param1 required visualization configurations.
+ * @returns intermediate visualization json data
+ */
+export const preprocessJsonData = (
+ visJson: Array,
+ { dimensions, series, breakdowns, span }: Partial
+) => {
+ const seriesFlattenedEntries = [];
+ forEach(visJson, (entry) => {
+ forEach(series, (sr) => {
+ let tabularVizData = {};
+ const serieKey = sr[CUSTOM_LABEL] ? sr[CUSTOM_LABEL] : `${sr.aggregation}(${sr.name})`;
+ let timespanDimension = [];
+ if (span) {
+ timespanDimension.push(getCompleteTimespanKey(span));
+ }
+ if (!isEmpty(serieKey)) {
+ const concatedXaxisLabel = [...timespanDimension, ...(dimensions)]
+ .map((dimension) => entry[dimension.name])
+ .join(',');
+ const concatedBreakdownLabel = breakdowns
+ ? breakdowns.map((breakdown) => entry[breakdown.name]).join(',')
+ : '';
+ tabularVizData = {
+ value: entry[serieKey],
+ x: concatedXaxisLabel,
+ breakdown: concatedBreakdownLabel,
+ aggName: serieKey,
+ };
+ } else {
+ tabularVizData = {
+ value: 0,
+ x: '',
+ breakdown: '',
+ };
+ }
+ seriesFlattenedEntries.push(tabularVizData);
+ });
+ });
+ return seriesFlattenedEntries;
+};
diff --git a/public/components/visualizations/charts/shared/shared_configs.ts b/public/components/visualizations/charts/shared/shared_configs.ts
index 062f38785..6730a5d60 100644
--- a/public/components/visualizations/charts/shared/shared_configs.ts
+++ b/public/components/visualizations/charts/shared/shared_configs.ts
@@ -16,7 +16,7 @@ export const getPlotlySharedConfigs = () => {
t: 50,
pad: 0,
},
- height: 500,
+ height: 1180,
legend: {
orientation: 'v',
traceorder: 'normal',
diff --git a/public/components/visualizations/charts/text/text_type.ts b/public/components/visualizations/charts/text/text_type.ts
index bc6b90ca9..183fe9235 100644
--- a/public/components/visualizations/charts/text/text_type.ts
+++ b/public/components/visualizations/charts/text/text_type.ts
@@ -17,21 +17,21 @@ export const createTextTypeDefinition = (params: any = {}) => ({
name: 'text',
type: 'text',
id: 'text',
- label: 'Text',
- fullLabel: 'Text',
- iconType: 'visText',
+ label: 'Markdown',
+ fulllabel: 'Markdown',
+ icontype: 'visText',
category: VIS_CATEGORY.BASICS,
selection: {
dataLoss: 'nothing',
},
icon: LensIconChartLine,
- categoryAxis: 'xaxis',
- seriesAxis: 'yaxis',
- editorConfig: {
+ categoryaxis: 'xaxis',
+ seriesaxis: 'yaxis',
+ editorconfig: {
panelTabs: [
{
id: 'data-panel',
- name: 'Data',
+ name: 'Style',
mapTo: 'dataConfig',
editor: VizDataPanel,
sections: [
@@ -46,7 +46,7 @@ export const createTextTypeDefinition = (params: any = {}) => ({
},
],
},
- visConfig: {
+ visconfig: {
layout: {
...sharedConfigs.layout,
...{
diff --git a/public/components/visualizations/charts/vis_types.ts b/public/components/visualizations/charts/vis_types.ts
index 0d3998547..2c761f2bd 100644
--- a/public/components/visualizations/charts/vis_types.ts
+++ b/public/components/visualizations/charts/vis_types.ts
@@ -4,7 +4,6 @@
*/
import { createBarTypeDefinition } from './bar/bar_type';
-import { createHorizontalBarTypeDefinition } from './bar/horizontal_bar_type';
import { createLineTypeDefinition } from './lines/line_type';
import { createPieTypeDefinition } from './pie/pie_type';
import { createHistogramVisDefinition } from './histogram/histogram_type';
@@ -14,10 +13,12 @@ import { createDatatableTypeDefinition } from './data_table/data_table_type';
import { createGaugeTypeDefinition } from './financial/gauge/gauge_type';
import { createTreeMapDefinition } from './maps/treemap_type';
import { createTextTypeDefinition } from './text/text_type';
+import { createLogsViewTypeDefinition } from './logs_view/logs_view_type';
+import { createMetricsTypeDefinition } from './metrics/metrics_type';
export const VIS_TYPES = {
bar: createBarTypeDefinition,
- horizontal_bar: createHorizontalBarTypeDefinition,
+ horizontal_bar: createBarTypeDefinition,
line: createLineTypeDefinition,
pie: createPieTypeDefinition,
histogram: createHistogramVisDefinition,
@@ -27,6 +28,9 @@ export const VIS_TYPES = {
heatmap: createMapsVisDefinition,
tree_map: createTreeMapDefinition,
text: createTextTypeDefinition,
+ scatter: createLineTypeDefinition,
+ logs_view: createLogsViewTypeDefinition,
+ metrics: createMetricsTypeDefinition,
};
export const getVisType = (visType: string, params: any = {}) => {
diff --git a/public/components/visualizations/plotly/plot.tsx b/public/components/visualizations/plotly/plot.tsx
index 1d7a7f1e9..bf1a61642 100644
--- a/public/components/visualizations/plotly/plot.tsx
+++ b/public/components/visualizations/plotly/plot.tsx
@@ -33,13 +33,6 @@ export function Plt(props: PltProps) {
const finalLayout = {
autosize: true,
- margin: {
- l: 30,
- r: 5,
- b: 30,
- t: 5,
- pad: 4,
- },
barmode: 'stack',
legend: {
orientation: 'h',
diff --git a/public/components/visualizations/visualization.tsx b/public/components/visualizations/visualization.tsx
index 329e6881b..75ba98241 100644
--- a/public/components/visualizations/visualization.tsx
+++ b/public/components/visualizations/visualization.tsx
@@ -7,6 +7,7 @@ import React from 'react';
import { isArray } from 'lodash';
import { VisualizationChart } from './visualization_chart';
import { EmptyPlaceholder } from '../event_analytics/explorer/visualizations/shared_components/empty_placeholder';
+import { VIS_CHART_TYPES } from '../../../common/constants/shared';
interface IVisualizationProps {}
@@ -23,10 +24,10 @@ export const Visualization = ({ visualizations }: IVisualizationProps) => {
return (
<>
- {isVizDataValid && isVizFieldValid ? (
+ {vis?.type === VIS_CHART_TYPES.LogsView || (isVizDataValid && isVizFieldValid) ? (
) : (
-
+
)}
>
);
diff --git a/public/components/visualizations/visualization_chart.tsx b/public/components/visualizations/visualization_chart.tsx
index 88bb34edd..cc5a667ff 100644
--- a/public/components/visualizations/visualization_chart.tsx
+++ b/public/components/visualizations/visualization_chart.tsx
@@ -4,31 +4,27 @@
*/
import React, { useMemo } from 'react';
-import { take, merge, isEmpty } from 'lodash';
interface IVisualizationChart {}
export const VisualizationChart = ({ visualizations }: IVisualizationChart) => {
- const { data, vis } = visualizations;
- const {
- metadata: { fields },
- } = visualizations?.data?.rawVizData;
+ const { vis } = visualizations;
const { layout = {}, config = {} } = visualizations?.data?.userConfigs;
const Visualization = visualizations?.vis?.component;
const finalFigureConfig = useMemo(() => {
return {
- ...vis.visConfig?.config,
+ ...vis.visconfig?.config,
...config,
};
}, [config, vis]);
- const finalFigureLayout = useMemo(() => {
+ const finalFigureLayout = () => {
return {
- ...vis.visConfig?.layout,
+ ...vis.visconfig?.layout,
...layout,
};
- }, [layout, vis]);
+ };
return (
{
public setup(core: CoreSetup): ObservabilitySetup {
uiSettingsService.init(core.uiSettings, core.notifications);
@@ -48,6 +48,7 @@ export class ObservabilityPlugin implements Plugin {
- console.error(error);
+ console.error('fetch error: ', error.body);
if (errorHandler) errorHandler(error);
});
};
diff --git a/test/event_analytics_constants.ts b/test/event_analytics_constants.ts
index a8a3a70a6..bcb75afaf 100644
--- a/test/event_analytics_constants.ts
+++ b/test/event_analytics_constants.ts
@@ -3,8 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { LONG_CHART_COLOR } from '../common/constants/shared';
+import { LONG_CHART_COLOR, VIS_CHART_TYPES } from '../common/constants/shared';
import { createBarTypeDefinition } from '../public/components/visualizations/charts/bar/bar_type';
+import { createGaugeTypeDefinition } from '../public/components/visualizations/charts/financial/gauge/gauge_type';
+import { createMetricsTypeDefinition } from '../public/components/visualizations/charts/metrics/metrics_type';
import {
SELECTED_FIELDS,
AVAILABLE_FIELDS as AVAILABLE_FIELDS_NAME,
@@ -15,111 +17,111 @@ import {
export const AVAILABLE_FIELDS = [
{
name: 'agent',
- type: 'string'
+ type: 'string',
},
{
name: 'bytes',
- type: 'long'
+ type: 'long',
},
{
name: 'clientip',
- type: 'ip'
+ type: 'ip',
},
{
name: 'event',
- type: 'struct'
+ type: 'struct',
},
{
name: 'extension',
- type: 'string'
+ type: 'string',
},
{
name: 'geo',
- type: 'struct'
+ type: 'struct',
},
{
name: 'host',
- type: 'string'
+ type: 'string',
},
{
name: 'index',
- type: 'string'
+ type: 'string',
},
{
name: 'ip',
- type: 'ip'
+ type: 'ip',
},
{
name: 'machine',
- type: 'struct'
+ type: 'struct',
},
{
name: 'memory',
- type: 'double'
+ type: 'double',
},
{
name: 'message',
- type: 'string'
+ type: 'string',
},
{
name: 'phpmemory',
- type: 'long'
+ type: 'long',
},
{
name: 'referer',
- type: 'string'
+ type: 'string',
},
{
name: 'request',
- type: 'string'
+ type: 'string',
},
{
name: 'response',
- type: 'string'
+ type: 'string',
},
{
name: 'tags',
- type: 'string'
+ type: 'string',
},
{
name: 'timestamp',
- type: 'timestamp'
+ type: 'timestamp',
},
{
name: 'url',
- type: 'string'
+ type: 'string',
},
{
name: 'utc_time',
- type: 'timestamp'
- }
+ type: 'timestamp',
+ },
];
export const QUERY_FIELDS = [
{
name: 'double_per_ip_bytes',
- type: 'long'
+ type: 'long',
},
{
name: 'host',
- type: 'text'
+ type: 'text',
},
{
name: 'ip_count',
- type: 'integer'
+ type: 'integer',
},
{
name: 'per_ip_bytes',
- type: 'long'
+ type: 'long',
},
{
name: 'resp_code',
- type: 'text'
+ type: 'text',
},
{
name: 'sum_bytes',
- type: 'long'
- }
+ type: 'long',
+ },
];
export const JSON_DATA = [
@@ -129,7 +131,7 @@ export const JSON_DATA = [
host: 'artifacts.opensearch.org',
resp_code: '404',
per_ip_bytes: 5803,
- double_per_ip_bytes: 11606
+ double_per_ip_bytes: 11606,
},
{
ip_count: 111,
@@ -137,7 +139,7 @@ export const JSON_DATA = [
host: 'www.opensearch.org',
resp_code: '404',
per_ip_bytes: 5050,
- double_per_ip_bytes: 10100
+ double_per_ip_bytes: 10100,
},
{
ip_count: 94,
@@ -145,7 +147,7 @@ export const JSON_DATA = [
host: 'artifacts.opensearch.org',
resp_code: '503',
per_ip_bytes: 0,
- double_per_ip_bytes: 0
+ double_per_ip_bytes: 0,
},
{
ip_count: 78,
@@ -153,7 +155,7 @@ export const JSON_DATA = [
host: 'www.opensearch.org',
resp_code: '503',
per_ip_bytes: 0,
- double_per_ip_bytes: 0
+ double_per_ip_bytes: 0,
},
{
ip_count: 43,
@@ -161,7 +163,7 @@ export const JSON_DATA = [
host: 'cdn.opensearch-opensearch-opensearch.org',
resp_code: '404',
per_ip_bytes: 5763,
- double_per_ip_bytes: 11526
+ double_per_ip_bytes: 11526,
},
{
ip_count: 34,
@@ -169,7 +171,7 @@ export const JSON_DATA = [
host: 'cdn.opensearch-opensearch-opensearch.org',
resp_code: '503',
per_ip_bytes: 0,
- double_per_ip_bytes: 0
+ double_per_ip_bytes: 0,
},
{
ip_count: 13,
@@ -177,7 +179,7 @@ export const JSON_DATA = [
host: 'opensearch-opensearch-opensearch.org',
resp_code: '404',
per_ip_bytes: 4441,
- double_per_ip_bytes: 8882
+ double_per_ip_bytes: 8882,
},
{
ip_count: 6,
@@ -185,8 +187,8 @@ export const JSON_DATA = [
host: 'opensearch-opensearch-opensearch.org',
resp_code: '503',
per_ip_bytes: 0,
- double_per_ip_bytes: 0
- }
+ double_per_ip_bytes: 0,
+ },
];
export const JSON_DATA_ALL = [
@@ -196,7 +198,8 @@ export const JSON_DATA_ALL = [
agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1',
extension: 'deb',
memory: 'null',
- geo: '{"srcdest":"IN:US","src":"IN","coordinates":{"lat":39.41042861,"lon":-88.8454325},"dest":"US"}',
+ geo:
+ '{"srcdest":"IN:US","src":"IN","coordinates":{"lat":39.41042861,"lon":-88.8454325},"dest":"US"}',
utc_time: '2021-11-14 00:39:02.912',
clientip: '223.87.60.27',
host: 'artifacts.opensearch.org',
@@ -205,12 +208,13 @@ export const JSON_DATA_ALL = [
timestamp: '2021-11-14 00:39:02.912',
ip: '223.87.60.27',
index: 'opensearch_dashboards_sample_data_logs',
- message: '223.87.60.27 - - [2018-07-22T00:39:02.912Z] "GET /opensearch/opensearch-1.0.0.deb_1 HTTP/1.1" 200 6219 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"',
+ message:
+ '223.87.60.27 - - [2018-07-22T00:39:02.912Z] "GET /opensearch/opensearch-1.0.0.deb_1 HTTP/1.1" 200 6219 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"',
url: 'https://artifacts.opensearch.org/downloads/opensearch/opensearch-1.0.0.deb_1',
tags: 'success',
bytes: 6219,
machine: '{"os":"win 8","ram":8589934592}',
- response: '200'
+ response: '200',
},
{
referer: 'http://www.opensearch-opensearch-opensearch.com/success/james-mcdivitt',
@@ -218,7 +222,8 @@ export const JSON_DATA_ALL = [
agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1',
extension: '',
memory: 'null',
- geo: '{"srcdest":"JP:IN","src":"JP","coordinates":{"lat":38.58338806,"lon":-86.46248778},"dest":"IN"}',
+ geo:
+ '{"srcdest":"JP:IN","src":"JP","coordinates":{"lat":38.58338806,"lon":-86.46248778},"dest":"IN"}',
utc_time: '2021-11-14 03:26:21.326',
clientip: '130.246.123.197',
host: 'www.opensearch.org',
@@ -227,7 +232,8 @@ export const JSON_DATA_ALL = [
timestamp: '2021-11-14 03:26:21.326',
ip: '130.246.123.197',
index: 'opensearch_dashboards_sample_data_logs',
- message: '130.246.123.197 - - [2018-07-22T03:26:21.326Z] "GET /beats/metricbeat_1 HTTP/1.1" 200 6850 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"',
+ message:
+ '130.246.123.197 - - [2018-07-22T03:26:21.326Z] "GET /beats/metricbeat_1 HTTP/1.1" 200 6850 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"',
url: 'https://www.opensearch.org/downloads/beats/metricbeat_1',
tags: 'success',
bytes: 6850,
@@ -237,10 +243,12 @@ export const JSON_DATA_ALL = [
{
referer: 'http://twitter.com/success/konstantin-feoktistov',
request: '/styles/main.css',
- agent: 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24',
+ agent:
+ 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24',
extension: 'css',
memory: 'null',
- geo: '{"srcdest":"CO:DE","src":"CO","coordinates":{"lat":36.96015,"lon":-78.18499861},"dest":"DE"}',
+ geo:
+ '{"srcdest":"CO:DE","src":"CO","coordinates":{"lat":36.96015,"lon":-78.18499861},"dest":"DE"}',
utc_time: '2021-11-14 03:30:25.131',
clientip: '120.49.143.213',
host: 'cdn.opensearch-opensearch-opensearch.org',
@@ -249,18 +257,19 @@ export const JSON_DATA_ALL = [
timestamp: '2021-11-14 03:30:25.131',
ip: '120.49.143.213',
index: 'opensearch_dashboards_sample_data_logs',
- message: '120.49.143.213 - - [2018-07-22T03:30:25.131Z] "GET /styles/main.css_1 HTTP/1.1" 503 0 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"',
+ message:
+ '120.49.143.213 - - [2018-07-22T03:30:25.131Z] "GET /styles/main.css_1 HTTP/1.1" 503 0 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"',
url: 'https://cdn.opensearch-opensearch-opensearch.org/styles/main.css_1',
tags: 'success',
bytes: 0,
machine: '{"os":"ios","ram":20401094656}',
- response: '503'
+ response: '503',
},
];
export const AGENT_FIELD = {
name: 'agent',
- type: 'string'
+ type: 'string',
};
export const SAVED_HISTORIES = [
@@ -271,21 +280,22 @@ export const SAVED_HISTORIES = [
savedVisualization: {
description: '',
name: 'Mock Flight count by destination save to panel',
- query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier',
+ query:
+ 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier',
type: 'bar',
selected_date_range: {
end: 'now',
start: 'now-15m',
- text: ''
+ text: '',
},
selected_fields: {
text: '',
- tokens: []
+ tokens: [],
},
selected_timestamp: {
name: 'timestamp',
- type: 'timestamp'
- }
+ type: 'timestamp',
+ },
},
tenant: '',
},
@@ -296,24 +306,25 @@ export const SAVED_HISTORIES = [
savedVisualization: {
description: '',
name: 'Mock Flight count by destination',
- query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier',
+ query:
+ 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier',
type: 'bar',
selected_date_range: {
end: 'now',
start: 'now-15m',
- text: ''
+ text: '',
},
selected_fields: {
text: '',
- tokens: []
+ tokens: [],
},
selected_timestamp: {
name: 'timestamp',
- type: 'timestamp'
- }
+ type: 'timestamp',
+ },
},
tenant: '',
- }
+ },
];
export const SELECTED_PANELS_OPTIONS = [
@@ -323,7 +334,7 @@ export const SELECTED_PANELS_OPTIONS = [
dateCreated: 1637781403888,
dateModified: 1637781403888,
id: 'uRZgU30B661cwDZT-ILw',
- name: '[Logs] Web traffic Panel'
+ name: '[Logs] Web traffic Panel',
},
},
{
@@ -332,9 +343,9 @@ export const SELECTED_PANELS_OPTIONS = [
dateCreated: 1637781403888,
dateModified: 1637781403888,
id: 'uRZgU30B661cwDZT-ILw',
- name: '[Logs] Web traffic Panel'
+ name: '[Logs] Web traffic Panel',
},
- }
+ },
];
export const DATA_GRID_ROWS = [
@@ -346,9 +357,9 @@ export const DATA_GRID_ROWS = [
DestAirportID: 'SYD',
DestCityName: 'Sydney',
DestCountry: 'AU',
- DestLocation: '{\"lat\":-33.94609833,\"lon\":151.177002}',
+ DestLocation: '{"lat":-33.94609833,"lon":151.177002}',
DestRegion: 'SE-BD',
- DestWeather: "Rain",
+ DestWeather: 'Rain',
DistanceKilometers: 16492.326,
DistanceMiles: 10247.856,
FlightDelay: 'false',
@@ -358,14 +369,14 @@ export const DATA_GRID_ROWS = [
FlightTimeHour: '17.179506930998397',
FlightTimeMin: 1030.7704,
Origin: 'Frankfurt am Main Airport',
- OriginAirportID: "FRA",
+ OriginAirportID: 'FRA',
OriginCityName: 'Frankfurt am Main',
OriginCountry: 'DE',
- OriginLocation: '{\"lat\":50.033333,\"lon\":8.570556}',
+ OriginLocation: '{"lat":50.033333,"lon":8.570556}',
OriginRegion: 'DE-HE',
OriginWeather: 'Sunny',
dayOfWeek: 0,
- timestamp: "2021-05-24 00:00:00"
+ timestamp: '2021-05-24 00:00:00',
},
{
AvgTicketPrice: 882.98267,
@@ -375,9 +386,9 @@ export const DATA_GRID_ROWS = [
DestAirportID: 'VE05',
DestCityName: 'Venice',
DestCountry: 'IT',
- DestLocation: '{\"lat\":45.505299,\"lon\":12.3519}',
+ DestLocation: '{"lat":45.505299,"lon":12.3519}',
DestRegion: 'IT-34',
- DestWeather: "Sunny",
+ DestWeather: 'Sunny',
DistanceKilometers: 8823.4,
DistanceMiles: 5482.6064,
FlightDelay: 'false',
@@ -387,82 +398,78 @@ export const DATA_GRID_ROWS = [
FlightTimeHour: '7.73982468459836',
FlightTimeMin: 464.3895,
Origin: 'Cape Town International Airport',
- OriginAirportID: "CPT",
+ OriginAirportID: 'CPT',
OriginCityName: 'Cape Town',
OriginCountry: 'ZA',
- OriginLocation: '{\"lat\":-33.96480179,\"lon\":18.60169983}',
+ OriginLocation: '{"lat":-33.96480179,"lon":18.60169983}',
OriginRegion: 'SE-BD',
OriginWeather: 'Clear',
dayOfWeek: 0,
- timestamp: "2021-05-24 18:27:00"
- }
+ timestamp: '2021-05-24 18:27:00',
+ },
];
export const SAMPLE_VISUALIZATIONS = {
data: {
'count()': [2549, 9337, 1173],
- 'span(timestamp,1M)': [
- '2021-05-01 00:00:00',
- '2021-06-01 00:00:00',
- '2021-07-01 00:00:00'
- ],
+ 'span(timestamp,1M)': ['2021-05-01 00:00:00', '2021-06-01 00:00:00', '2021-07-01 00:00:00'],
},
jsonData: [
{
'count()': 2549,
- 'span(timestamp,1M)': '2021-05-01 00:00:00'
+ 'span(timestamp,1M)': '2021-05-01 00:00:00',
},
{
'count()': 9337,
- 'span(timestamp,1M)': '2021-06-01 00:00:00'
+ 'span(timestamp,1M)': '2021-06-01 00:00:00',
},
{
'count()': 2549,
- 'span(timestamp,1M)': '2021-07-01 00:00:00'
- }
+ 'span(timestamp,1M)': '2021-07-01 00:00:00',
+ },
],
metadata: {
fields: [
{
name: 'count()',
- type: 'integer'
+ type: 'integer',
},
{
name: 'span(timestamp,1M)',
- type: 'timestamp'
- }
- ]
- }
+ type: 'timestamp',
+ },
+ ],
+ },
};
export const VISUALIZATION_TYPES = [
{
- fullLabel: 'Bar',
+ fulllabel: 'Bar',
id: 'bar',
label: 'bar',
selection: {
- dataLoss: 'nothing'
+ dataLoss: 'nothing',
},
- visualizationId: 'vis-bar-6636'
+ visualizationid: 'vis-bar-6636',
},
{
- fullLabel: 'H. Bar',
+ fulllabel: 'H. Bar',
id: 'horizontal_bar',
label: 'H. Bar',
selection: {
- dataLoss: 'nothing'
+ dataLoss: 'nothing',
},
- visualizationId: 'vis-bar-6637'
+ visualizationid: 'vis-bar-6637',
},
{
- fullLabel: 'Line',
+ fulllabel: 'Line',
id: 'line',
label: 'line',
selection: {
- dataLoss: 'nothing'
+ dataLoss: 'nothing',
},
- visualizationId: 'vis-bar-6638'
- }
+ visualizationid: 'vis-bar-6638',
+ },
];
export const LAYOUT_CONFIG = {
@@ -488,48 +495,72 @@ export const EXPLORER_FIELDS = {
export const EXPLORER_VISUALIZATIONS = {
data: {
'count()': [154, 1753, 116, 468, 1964, 219],
- tags:["error", "info", "login", "security", "success", "warning"],
+ tags: ['error', 'info', 'login', 'security', 'success', 'warning'],
},
jsonData: [
- {'count()': 154, tags: "error"},
- {'count()': 1753, tags: "info"},
- {'count()': 116, tags: "login"},
- {'count()': 468, tags: "security"},
- {'count()': 1964, tags: "success"},
- {'count()': 219, tags: "warning"}
+ { 'count()': 154, tags: 'error' },
+ { 'count()': 1753, tags: 'info' },
+ { 'count()': 116, tags: 'login' },
+ { 'count()': 468, tags: 'security' },
+ { 'count()': 1964, tags: 'success' },
+ { 'count()': 219, tags: 'warning' },
],
metadata: {
fields: [
- {name: "count()", type: "integer"},
- {name: "tags", type: "text"}
+ { name: 'count()', type: 'integer' },
+ { name: 'tags', type: 'text' },
],
size: 6,
- status: 200
+ status: 200,
},
};
+export const VALUE_OPTIONS = {
+ dimensions: [{ name: 'tags', type: 'text', label: 'tags' }],
+ metrics: [{ name: 'count()', type: 'integer', label: 'count()', side: 'left' }],
+};
+
export const TEST_VISUALIZATIONS_DATA = {
data: {
- appData: {fromApp: false},
+ appData: { fromApp: false },
defaultAxes: {},
indexFields: EXPLORER_FIELDS,
query: {},
rawVizData: EXPLORER_VISUALIZATIONS,
- userConfigs: {}
+ userConfigs: {
+ dataConfig: {
+ valueOptions: VALUE_OPTIONS,
+ },
+ },
},
- vis: createBarTypeDefinition({})
+ vis: createBarTypeDefinition({}),
};
export const PIE_TEST_VISUALIZATIONS_DATA = {
data: {
...TEST_VISUALIZATIONS_DATA.data,
defaultAxes: {
- xaxis: [{name: "tags", type: "text"}],
- yaxis: [{name: "count()", type: "integer"}]
- }
+ xaxis: [{ name: 'tags', type: 'text' }],
+ yaxis: [{ name: 'count()', type: 'integer' }],
+ },
},
vis: {
- ...TEST_VISUALIZATIONS_DATA.vis
- }
+ ...TEST_VISUALIZATIONS_DATA.vis,
+ },
+};
+
+export const GAUGE_TEST_VISUALIZATIONS_DATA = {
+ ...TEST_VISUALIZATIONS_DATA,
+ vis: createGaugeTypeDefinition(),
};
+export const METRICS_TEST_VISUALIZATIONS_DATA = {
+ ...TEST_VISUALIZATIONS_DATA,
+ vis: createMetricsTypeDefinition({}),
+};
+export const HORIZONTAL_BAR_TEST_VISUALIZATIONS_DATA = {
+ ...TEST_VISUALIZATIONS_DATA,
+ vis: createBarTypeDefinition({
+ type: VIS_CHART_TYPES.HorizontalBar,
+ }),
+};
diff --git a/test/panels_constants.tsx b/test/panels_constants.tsx
index 1b1362bf2..b79dde38c 100644
--- a/test/panels_constants.tsx
+++ b/test/panels_constants.tsx
@@ -19,6 +19,40 @@ export const sampleSavedVisualization = {
},
};
+export const sampleSavedVisualizationForHorizontalBar = {
+ id: 'zgBSfYIBi5fNIzQywlQZ',
+ name: 'Horizontal',
+ query: 'source = opensearch_dashboards_sample_data_logs | stats avg(machine.ram) by machine.os',
+ type: 'horizontal_bar',
+ timeField: 'timestamp',
+};
+
+export const sampleSavedVisualizationForLine = {
+ id: '4wANiYIBi5fNIzQySlRB',
+ name: '[Logs] Average ram usage per day by windows os ',
+ query:
+ "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1d)",
+ type: 'line',
+ timeField: 'timestamp',
+};
+
+export const sampleSavedVisualizationForTreeMap = {
+ id: '5QANiYIBi5fNIzQySlRL',
+ name: 'TreeMap1111',
+ query: 'source = opensearch_dashboards_sample_data_logs | stats count() by tags',
+ type: 'tree_map',
+ timeField: 'timestamp',
+};
+
+export const sampleSavedVisualizationForPie = {
+ id: '4gANiYIBi5fNIzQySlQ5',
+ name: 'PieChart',
+ query:
+ "source = opensearch_dashboards_sample_data_logs | where machine.os='osx' or machine.os='ios' | stats avg(machine.ram) by span(timestamp,1d)",
+ type: 'pie',
+ timeField: 'timestamp',
+};
+
export const samplePPLResponse = {
data: {
'avg(FlightDelayMin)': [
diff --git a/yarn.lock b/yarn.lock
index c205c9bf8..24d2a17b4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -143,6 +143,11 @@
debug "^3.1.0"
lodash.once "^4.1.1"
+"@danieldietrich/copy@^0.4.2":
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/@danieldietrich/copy/-/copy-0.4.2.tgz#c1cabfa499d8b473ba95413c446c1c1efae64d24"
+ integrity sha512-ZVNZIrgb2KeomfNahP77rL445ho6aQj0HHqU6hNlQ61o4rhvca+NS+ePj0d82zQDq2UPk1mjVZBTXgP+ErsDgw==
+
"@hypnosphi/create-react-context@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6"
@@ -325,6 +330,18 @@ acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+ag-grid-community@^27.3.0:
+ version "27.3.0"
+ resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-27.3.0.tgz#b1e94a58026aaf2f0cd7920e35833325b5e762c7"
+ integrity sha512-R5oZMXEHXnOLrmhn91J8lR0bv6IAnRcU6maO+wKLMJxffRWaAYFAuw1jt7bdmcKCv8c65F6LEBx4ykSOALa9vA==
+
+ag-grid-react@^27.3.0:
+ version "27.3.0"
+ resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-27.3.0.tgz#fe06647653f8b0b349b8e613aab8ea2e07915562"
+ integrity sha512-2bs9YfJ/shvBZQLLjny4NFvht+ic6VtpTPO0r3bHHOhlL3Fjx2rGvS6AHSwfvu+kJacHCta30PjaEbX8T3UDyw==
+ dependencies:
+ prop-types "^15.8.1"
+
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -384,6 +401,21 @@ ansi-to-react@^6.0.5:
anser "^1.4.1"
escape-carriage "^1.3.0"
+antlr4@4.8.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.8.0.tgz#f938ec171be7fc2855cd3a533e87647185b32b6a"
+ integrity sha512-en/MxQ4OkPgGJQ3wD/muzj1uDnFSzdFIhc2+c6bHZokWkuBb6RRvFjpWhPxWLbgQvaEzldJZ0GSQpfSAaE3hqg==
+
+antlr4ts-cli@^0.5.0-alpha.4:
+ version "0.5.0-alpha.4"
+ resolved "https://registry.yarnpkg.com/antlr4ts-cli/-/antlr4ts-cli-0.5.0-alpha.4.tgz#f3bfc37f10131e78d7b981c397a2aaa0450b67f6"
+ integrity sha512-lVPVBTA2CVHRYILSKilL6Jd4hAumhSZZWA7UbQNQrmaSSj7dPmmYaN4bOmZG79cOy0lS00i4LY68JZZjZMWVrw==
+
+antlr4ts@^0.5.0-alpha.4:
+ version "0.5.0-alpha.4"
+ resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a"
+ integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==
+
any-observable@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
@@ -1285,6 +1317,18 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@^7.1.7:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
global-dirs@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
@@ -1881,6 +1925,13 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
minimist@^1.2.5, minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
@@ -1901,9 +1952,9 @@ mkdirp@^0.5.4:
minimist "^1.2.5"
moment@^2.27.0:
- version "2.29.2"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4"
- integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==
+ version "2.29.4"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
+ integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
ms@2.0.0:
version "2.0.0"
@@ -2099,6 +2150,17 @@ popper.js@^1.14.4, popper.js@^1.16.1:
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
+postinstall@^0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/postinstall/-/postinstall-0.7.4.tgz#ab8de950f5d0350d753747c8b2606e347c80b3c8"
+ integrity sha512-jrItKnoJJCY6wuhP/LpTy5KyWJYUOOs+2477PUAXDCrJOZX2vgzCD3jgXawhJG4qdFxvAepaJLum1trieFbEuw==
+ dependencies:
+ "@danieldietrich/copy" "^0.4.2"
+ glob "^7.1.7"
+ minimist "^1.2.5"
+ resolve-from "^5.0.0"
+ resolve-pkg "^2.0.0"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -2133,6 +2195,15 @@ prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
object-assign "^4.1.1"
react-is "^16.8.1"
+prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
property-information@^5.0.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
@@ -2216,7 +2287,7 @@ react-graph-vis@^1.0.5:
vis-data "^7.1.2"
vis-network "^9.0.0"
-react-is@^16.8.1, react-is@^16.8.6:
+react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -2248,6 +2319,13 @@ react-markdown@^4.0.0:
unist-util-visit "^1.3.0"
xtend "^4.0.1"
+react-paginate@^8.1.3:
+ version "8.1.3"
+ resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.1.3.tgz#cd6f3cb8a56b47617a61a6a52e3d7c662ad9b91b"
+ integrity sha512-zBp80DBRcaeBnAeHUfbGKD0XHfbGNUolQ+S60Ymfs8o7rusYaJYZMAt1j93ADDNLlzRmJ0tMF/NeTlcdKf7dlQ==
+ dependencies:
+ prop-types "^15.6.1"
+
react-plotly.js@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/react-plotly.js/-/react-plotly.js-2.5.1.tgz#11182bf599ef11a0dbfcd171c6f5645535a2b486"
@@ -2401,6 +2479,18 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41"
+ integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==
+ dependencies:
+ resolve-from "^5.0.0"
+
restore-cursor@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"