mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 20:19:13 +08:00
feat: added support for bar chart stacking (#5138)
* feat: stacked bars uplot poc * feat: stacked bars uplot poc * feat: reverse the legend order * fix: tooltip * feat: added bands * feat: added bands calculation function * feat: code cleanup and added toggle for stacked and unstacked bars * feat: minor fixes and better naming * feat: fix jest test cases * feat: fix data on view mode of bar chart stacked * feat: make transulecent colors * fix: build issues * fix: legend issues in bar chart edit mode * feat: added implementation details and refactored code in tooltip plugin * fix: added missing return statement * fix: eslint prettier issues * fix: legend visibility issues on view mode * fix: legend visibility issues on view mode * feat: added info text * fix: add info text only in full view mode * fix: issue with zero index
This commit is contained in:
parent
6af5aa0253
commit
f2aba5035a
@ -204,7 +204,7 @@ function FullView({
|
||||
<div
|
||||
className={cx('graph-container', {
|
||||
disabled: isDashboardLocked,
|
||||
'height-widget': widget?.mergeAllActiveQueries,
|
||||
'height-widget': widget?.mergeAllActiveQueries || widget?.stackedBarChart,
|
||||
'list-graph-container': isListView,
|
||||
})}
|
||||
ref={fullViewRef}
|
||||
|
@ -241,6 +241,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
.stack-chart {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
|
||||
.label {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-family: 'Space Mono';
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 138.462% */
|
||||
letter-spacing: 0.52px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.bucket-config {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
|
@ -132,3 +132,17 @@ export const panelTypeVsColumnUnitPreferences: {
|
||||
[PANEL_TYPES.HISTOGRAM]: false,
|
||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
||||
} as const;
|
||||
|
||||
export const panelTypeVsStackingChartPreferences: {
|
||||
[key in PANEL_TYPES]: boolean;
|
||||
} = {
|
||||
[PANEL_TYPES.TIME_SERIES]: false,
|
||||
[PANEL_TYPES.VALUE]: false,
|
||||
[PANEL_TYPES.TABLE]: false,
|
||||
[PANEL_TYPES.LIST]: false,
|
||||
[PANEL_TYPES.PIE]: false,
|
||||
[PANEL_TYPES.BAR]: true,
|
||||
[PANEL_TYPES.TRACE]: false,
|
||||
[PANEL_TYPES.HISTOGRAM]: false,
|
||||
[PANEL_TYPES.EMPTY_WIDGET]: false,
|
||||
} as const;
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
panelTypeVsFillSpan,
|
||||
panelTypeVsPanelTimePreferences,
|
||||
panelTypeVsSoftMinMax,
|
||||
panelTypeVsStackingChartPreferences,
|
||||
panelTypeVsThreshold,
|
||||
panelTypeVsYAxisUnit,
|
||||
} from './constants';
|
||||
@ -48,6 +49,8 @@ function RightContainer({
|
||||
selectedGraph,
|
||||
bucketCount,
|
||||
bucketWidth,
|
||||
stackedBarChart,
|
||||
setStackedBarChart,
|
||||
setBucketCount,
|
||||
setBucketWidth,
|
||||
setSelectedTime,
|
||||
@ -87,6 +90,8 @@ function RightContainer({
|
||||
const allowYAxisUnit = panelTypeVsYAxisUnit[selectedGraph];
|
||||
const allowCreateAlerts = panelTypeVsCreateAlert[selectedGraph];
|
||||
const allowBucketConfig = panelTypeVsBucketConfig[selectedGraph];
|
||||
const allowStackingBarChart =
|
||||
panelTypeVsStackingChartPreferences[selectedGraph];
|
||||
const allowPanelTimePreference =
|
||||
panelTypeVsPanelTimePreferences[selectedGraph];
|
||||
|
||||
@ -231,6 +236,17 @@ function RightContainer({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{allowStackingBarChart && (
|
||||
<section className="stack-chart">
|
||||
<Typography.Text className="label">Stack series</Typography.Text>
|
||||
<Switch
|
||||
checked={stackedBarChart}
|
||||
size="small"
|
||||
onChange={(checked): void => setStackedBarChart(checked)}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{allowBucketConfig && (
|
||||
<section className="bucket-config">
|
||||
<Typography.Text className="label">Number of buckets</Typography.Text>
|
||||
@ -312,6 +328,8 @@ interface RightContainerProps {
|
||||
setSelectedTime: Dispatch<SetStateAction<timePreferance>>;
|
||||
selectedTime: timePreferance;
|
||||
yAxisUnit: string;
|
||||
stackedBarChart: boolean;
|
||||
setStackedBarChart: Dispatch<SetStateAction<boolean>>;
|
||||
bucketWidth: number;
|
||||
bucketCount: number;
|
||||
combineHistogram: boolean;
|
||||
|
@ -126,6 +126,10 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
const [stacked, setStacked] = useState<boolean>(
|
||||
selectedWidget?.isStacked || false,
|
||||
);
|
||||
|
||||
const [stackedBarChart, setStackedBarChart] = useState<boolean>(
|
||||
selectedWidget?.stackedBarChart || false,
|
||||
);
|
||||
const [opacity, setOpacity] = useState<string>(selectedWidget?.opacity || '1');
|
||||
const [thresholds, setThresholds] = useState<ThresholdProps[]>(
|
||||
selectedWidget?.thresholds || [],
|
||||
@ -195,6 +199,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
fillSpans: isFillSpans,
|
||||
columnUnits,
|
||||
bucketCount,
|
||||
stackedBarChart,
|
||||
bucketWidth,
|
||||
mergeAllActiveQueries: combineHistogram,
|
||||
selectedLogFields,
|
||||
@ -219,6 +224,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
bucketWidth,
|
||||
bucketCount,
|
||||
combineHistogram,
|
||||
stackedBarChart,
|
||||
]);
|
||||
|
||||
const closeModal = (): void => {
|
||||
@ -307,6 +313,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
opacity: selectedWidget?.opacity || '1',
|
||||
nullZeroValues: selectedWidget?.nullZeroValues || 'zero',
|
||||
title: selectedWidget?.title,
|
||||
stackedBarChart: selectedWidget?.stackedBarChart || false,
|
||||
yAxisUnit: selectedWidget?.yAxisUnit,
|
||||
panelTypes: graphType,
|
||||
query: currentQuery,
|
||||
@ -332,6 +339,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
opacity: selectedWidget?.opacity || '1',
|
||||
nullZeroValues: selectedWidget?.nullZeroValues || 'zero',
|
||||
title: selectedWidget?.title,
|
||||
stackedBarChart: selectedWidget?.stackedBarChart || false,
|
||||
yAxisUnit: selectedWidget?.yAxisUnit,
|
||||
panelTypes: graphType,
|
||||
query: currentQuery,
|
||||
@ -532,6 +540,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
setDescription={setDescription}
|
||||
stacked={stacked}
|
||||
setStacked={setStacked}
|
||||
stackedBarChart={stackedBarChart}
|
||||
setStackedBarChart={setStackedBarChart}
|
||||
opacity={opacity}
|
||||
yAxisUnit={yAxisUnit}
|
||||
columnUnits={columnUnits}
|
||||
|
@ -0,0 +1,4 @@
|
||||
.info-text {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
import './UplotPanelWrapper.styles.scss';
|
||||
|
||||
import { Alert } from 'antd';
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import Uplot from 'components/Uplot';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@ -8,6 +11,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { cloneDeep, isEqual, isUndefined } from 'lodash-es';
|
||||
import _noop from 'lodash-es/noop';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
@ -35,6 +39,8 @@ function UplotPanelWrapper({
|
||||
const [maxTimeScale, setMaxTimeScale] = useState<number>();
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
|
||||
const [hiddenGraph, setHiddenGraph] = useState<{ [key: string]: boolean }>();
|
||||
|
||||
useEffect(() => {
|
||||
if (toScrollWidgetId === widget.id) {
|
||||
graphRef.current?.scrollIntoView({
|
||||
@ -78,8 +84,26 @@ function UplotPanelWrapper({
|
||||
const chartData = getUPlotChartData(
|
||||
queryResponse?.data?.payload,
|
||||
widget.fillSpans,
|
||||
widget?.stackedBarChart,
|
||||
hiddenGraph,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (widget.panelTypes === PANEL_TYPES.BAR && widget?.stackedBarChart) {
|
||||
const graphV = cloneDeep(graphVisibility)?.slice(1);
|
||||
const isSomeSelectedLegend = graphV?.some((v) => v === false);
|
||||
if (isSomeSelectedLegend) {
|
||||
const hiddenIndex = graphV?.findIndex((v) => v === true);
|
||||
if (!isUndefined(hiddenIndex) && hiddenIndex !== -1) {
|
||||
const updatedHiddenGraph = { [hiddenIndex]: true };
|
||||
if (!isEqual(hiddenGraph, updatedHiddenGraph)) {
|
||||
setHiddenGraph(updatedHiddenGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [graphVisibility, hiddenGraph, widget.panelTypes, widget?.stackedBarChart]);
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
getUPlotChartOptions({
|
||||
@ -99,6 +123,9 @@ function UplotPanelWrapper({
|
||||
setGraphsVisibilityStates: setGraphVisibility,
|
||||
panelType: selectedGraph || widget.panelTypes,
|
||||
currentQuery,
|
||||
stackBarChart: widget?.stackedBarChart,
|
||||
hiddenGraph,
|
||||
setHiddenGraph,
|
||||
}),
|
||||
[
|
||||
widget?.id,
|
||||
@ -107,6 +134,7 @@ function UplotPanelWrapper({
|
||||
widget.softMax,
|
||||
widget.softMin,
|
||||
widget.panelTypes,
|
||||
widget?.stackedBarChart,
|
||||
queryResponse.data?.payload,
|
||||
containerDimensions,
|
||||
isDarkMode,
|
||||
@ -118,15 +146,23 @@ function UplotPanelWrapper({
|
||||
setGraphVisibility,
|
||||
selectedGraph,
|
||||
currentQuery,
|
||||
hiddenGraph,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%', width: '100%' }} ref={graphRef}>
|
||||
<Uplot options={options} data={chartData} ref={lineChartRef} />
|
||||
{isFullViewMode && setGraphVisibility && (
|
||||
{widget?.stackedBarChart && isFullViewMode && (
|
||||
<Alert
|
||||
message="Selecting multiple legends is currently not supported in case of stacked bar charts"
|
||||
type="info"
|
||||
className="info-text"
|
||||
/>
|
||||
)}
|
||||
{isFullViewMode && setGraphVisibility && !widget?.stackedBarChart && (
|
||||
<GraphManager
|
||||
data={chartData}
|
||||
data={getUPlotChartData(queryResponse?.data?.payload, widget.fillSpans)}
|
||||
name={widget.id}
|
||||
options={options}
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
|
@ -10,9 +10,11 @@ import { saveLegendEntriesToLocalStorage } from 'container/GridCardLayout/GridCa
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { Dimensions } from 'hooks/useDimensions';
|
||||
import { convertValue } from 'lib/getConvertedValue';
|
||||
import { cloneDeep, isUndefined } from 'lodash-es';
|
||||
import _noop from 'lodash-es/noop';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryData, QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import onClickPlugin, { OnClickPluginOpts } from './plugins/onClickPlugin';
|
||||
@ -42,6 +44,82 @@ export interface GetUPlotChartOptions {
|
||||
softMin: number | null;
|
||||
softMax: number | null;
|
||||
currentQuery?: Query;
|
||||
stackBarChart?: boolean;
|
||||
hiddenGraph?: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
setHiddenGraph?: Dispatch<
|
||||
SetStateAction<{
|
||||
[key: string]: boolean;
|
||||
}>
|
||||
>;
|
||||
}
|
||||
|
||||
/** the function converts series A , series B , series C to
|
||||
* series A , series A + series B , series A + series B + series C
|
||||
* which helps us to always ensure the bar in the front is always
|
||||
* of the smallest value.
|
||||
*/
|
||||
|
||||
function getStackedSeries(apiResponse: QueryData[]): QueryData[] {
|
||||
const series = cloneDeep(apiResponse);
|
||||
|
||||
for (let i = series.length - 2; i >= 0; i--) {
|
||||
const { values } = series[i];
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
values[j][1] = String(
|
||||
parseFloat(values[j][1]) + parseFloat(series[i + 1].values[j][1]),
|
||||
);
|
||||
}
|
||||
|
||||
series[i].values = values;
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
/** this does the exact same operations as the function above for a different
|
||||
* response format.
|
||||
*/
|
||||
function getStackedSeriesQueryFormat(apiResponse: QueryData[]): QueryData[] {
|
||||
const series = cloneDeep(apiResponse);
|
||||
|
||||
for (let i = series.length - 2; i >= 0; i--) {
|
||||
const { values } = series[i];
|
||||
for (let j = 0; j < values.length; j++) {
|
||||
values[j].value = String(
|
||||
parseFloat(values[j].value) + parseFloat(series[i + 1].values[j].value),
|
||||
);
|
||||
}
|
||||
|
||||
series[i].values = values;
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
function getStackedSeriesYAxis(apiResponse: QueryDataV3[]): QueryDataV3[] {
|
||||
const series = cloneDeep(apiResponse);
|
||||
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
series[i].series = getStackedSeriesQueryFormat(series[i].series);
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
/**
|
||||
* here we define the different series bands which should get highlighted based
|
||||
* on cursor hover. basically the to and the from destination of a particular band.
|
||||
*/
|
||||
function getBands(series): any[] {
|
||||
const bands = [];
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
bands.push({
|
||||
series: [i === 0 ? -1 : i, i + 1],
|
||||
});
|
||||
}
|
||||
return bands;
|
||||
}
|
||||
|
||||
export const getUPlotChartOptions = ({
|
||||
@ -61,9 +139,18 @@ export const getUPlotChartOptions = ({
|
||||
softMin,
|
||||
panelType,
|
||||
currentQuery,
|
||||
stackBarChart: stackChart,
|
||||
hiddenGraph,
|
||||
setHiddenGraph,
|
||||
}: GetUPlotChartOptions): uPlot.Options => {
|
||||
const timeScaleProps = getXAxisScale(minTimeScale, maxTimeScale);
|
||||
|
||||
const stackBarChart = stackChart && isUndefined(hiddenGraph);
|
||||
|
||||
const series = getStackedSeries(apiResponse?.data?.result || []);
|
||||
|
||||
const bands = stackBarChart ? getBands(series) : null;
|
||||
|
||||
return {
|
||||
id,
|
||||
width: dimensions.width,
|
||||
@ -91,6 +178,7 @@ export const getUPlotChartOptions = ({
|
||||
},
|
||||
},
|
||||
padding: [16, 16, 8, 8],
|
||||
bands,
|
||||
scales: {
|
||||
x: {
|
||||
spanGaps: true,
|
||||
@ -99,7 +187,9 @@ export const getUPlotChartOptions = ({
|
||||
y: {
|
||||
...getYAxisScale({
|
||||
thresholds,
|
||||
series: apiResponse?.data?.newResult?.data?.result || [],
|
||||
series: stackBarChart
|
||||
? getStackedSeriesYAxis(apiResponse?.data?.newResult?.data?.result || [])
|
||||
: apiResponse?.data?.newResult?.data?.result || [],
|
||||
yAxisUnit,
|
||||
softMax,
|
||||
softMin,
|
||||
@ -107,7 +197,7 @@ export const getUPlotChartOptions = ({
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
tooltipPlugin({ apiResponse, yAxisUnit }),
|
||||
tooltipPlugin({ apiResponse, yAxisUnit, stackBarChart }),
|
||||
onClickPlugin({
|
||||
onClick: onClickHandler,
|
||||
}),
|
||||
@ -192,6 +282,17 @@ export const getUPlotChartOptions = ({
|
||||
const seriesArray = Array.from(seriesEls);
|
||||
seriesArray.forEach((seriesEl, index) => {
|
||||
seriesEl.addEventListener('click', () => {
|
||||
if (stackChart) {
|
||||
setHiddenGraph((prev) => {
|
||||
if (isUndefined(prev)) {
|
||||
return { [index]: true };
|
||||
}
|
||||
if (prev[index] === true) {
|
||||
return undefined;
|
||||
}
|
||||
return { [index]: true };
|
||||
});
|
||||
}
|
||||
if (graphsVisibilityStates) {
|
||||
setGraphsVisibilityStates?.((prev) => {
|
||||
const newGraphVisibilityStates = [...prev];
|
||||
@ -221,11 +322,16 @@ export const getUPlotChartOptions = ({
|
||||
],
|
||||
},
|
||||
series: getSeries({
|
||||
apiResponse,
|
||||
series:
|
||||
stackBarChart && isUndefined(hiddenGraph)
|
||||
? series
|
||||
: apiResponse?.data?.result,
|
||||
widgetMetaData: apiResponse?.data.result,
|
||||
graphsVisibilityStates,
|
||||
panelType,
|
||||
currentQuery,
|
||||
stackBarChart,
|
||||
hiddenGraph,
|
||||
}),
|
||||
axes: getAxes(isDarkMode, yAxisUnit),
|
||||
};
|
||||
|
@ -22,6 +22,19 @@ interface UplotTooltipDataProps {
|
||||
queryName: string;
|
||||
}
|
||||
|
||||
function getTooltipBaseValue(
|
||||
data: any[],
|
||||
index: number,
|
||||
idx: number,
|
||||
stackBarChart: boolean | undefined,
|
||||
): any {
|
||||
if (stackBarChart && index + 1 < data.length) {
|
||||
return data[index][idx] - data[index + 1][idx];
|
||||
}
|
||||
|
||||
return data[index][idx];
|
||||
}
|
||||
|
||||
const generateTooltipContent = (
|
||||
seriesList: any[],
|
||||
data: any[],
|
||||
@ -31,6 +44,7 @@ const generateTooltipContent = (
|
||||
isBillingUsageGraphs?: boolean,
|
||||
isHistogramGraphs?: boolean,
|
||||
isMergedSeries?: boolean,
|
||||
stackBarChart?: boolean,
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
): HTMLElement => {
|
||||
const container = document.createElement('div');
|
||||
@ -67,7 +81,8 @@ const generateTooltipContent = (
|
||||
unit = '',
|
||||
} = seriesList[index - 1] || {};
|
||||
|
||||
const value = data[index][idx];
|
||||
const value = getTooltipBaseValue(data, index, idx, stackBarChart);
|
||||
|
||||
const dataIngested = quantity[idx];
|
||||
const label = isMergedSeries
|
||||
? ''
|
||||
@ -201,6 +216,7 @@ type ToolTipPluginProps = {
|
||||
isBillingUsageGraphs?: boolean;
|
||||
isHistogramGraphs?: boolean;
|
||||
isMergedSeries?: boolean;
|
||||
stackBarChart?: boolean;
|
||||
};
|
||||
|
||||
const tooltipPlugin = ({
|
||||
@ -209,6 +225,7 @@ const tooltipPlugin = ({
|
||||
isBillingUsageGraphs,
|
||||
isHistogramGraphs,
|
||||
isMergedSeries,
|
||||
stackBarChart,
|
||||
}: ToolTipPluginProps): any => {
|
||||
let over: HTMLElement;
|
||||
let bound: HTMLElement;
|
||||
@ -272,6 +289,7 @@ const tooltipPlugin = ({
|
||||
isBillingUsageGraphs,
|
||||
isHistogramGraphs,
|
||||
isMergedSeries,
|
||||
stackBarChart,
|
||||
);
|
||||
overlay.appendChild(content);
|
||||
placement(overlay, anchor, 'right', 'start', { bound });
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import getLabelName from 'lib/getLabelName';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
|
||||
@ -28,16 +28,18 @@ const paths = (
|
||||
};
|
||||
|
||||
const getSeries = ({
|
||||
apiResponse,
|
||||
series,
|
||||
widgetMetaData,
|
||||
graphsVisibilityStates,
|
||||
panelType,
|
||||
hiddenGraph,
|
||||
}: GetSeriesProps): uPlot.Options['series'] => {
|
||||
const configurations: uPlot.Series[] = [
|
||||
{ label: 'Timestamp', stroke: 'purple' },
|
||||
];
|
||||
|
||||
const seriesList = apiResponse?.data.result || [];
|
||||
const seriesList = series || [];
|
||||
|
||||
const newGraphVisibilityStates = graphsVisibilityStates?.slice(1);
|
||||
|
||||
for (let i = 0; i < seriesList?.length; i += 1) {
|
||||
@ -64,7 +66,12 @@ const getSeries = ({
|
||||
panelType && panelType === PANEL_TYPES.BAR
|
||||
? null
|
||||
: lineInterpolations.spline,
|
||||
show: newGraphVisibilityStates ? newGraphVisibilityStates[i] : true,
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
show: newGraphVisibilityStates
|
||||
? newGraphVisibilityStates[i]
|
||||
: !isUndefined(hiddenGraph)
|
||||
? hiddenGraph[i]
|
||||
: true,
|
||||
label,
|
||||
fill: panelType && panelType === PANEL_TYPES.BAR ? `${color}40` : undefined,
|
||||
stroke: color,
|
||||
@ -84,11 +91,15 @@ const getSeries = ({
|
||||
};
|
||||
|
||||
export type GetSeriesProps = {
|
||||
apiResponse?: MetricRangePayloadProps;
|
||||
series?: QueryData[];
|
||||
widgetMetaData: QueryData[];
|
||||
graphsVisibilityStates?: boolean[];
|
||||
panelType?: PANEL_TYPES;
|
||||
currentQuery?: Query;
|
||||
stackBarChart?: boolean;
|
||||
hiddenGraph?: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export default getSeries;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { cloneDeep, isUndefined } from 'lodash-es';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
|
||||
@ -62,9 +63,25 @@ function fillMissingXAxisTimestamps(
|
||||
);
|
||||
}
|
||||
|
||||
function getStackedSeries(val: any): any {
|
||||
const series = cloneDeep(val) || [];
|
||||
|
||||
for (let i = series.length - 2; i >= 0; i--) {
|
||||
for (let j = 0; j < series[i].length; j++) {
|
||||
series[i][j] += series[i + 1][j];
|
||||
}
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
export const getUPlotChartData = (
|
||||
apiResponse?: MetricRangePayloadProps,
|
||||
fillSpans?: boolean,
|
||||
stackedBarChart?: boolean,
|
||||
hiddenGraph?: {
|
||||
[key: string]: boolean;
|
||||
},
|
||||
): any[] => {
|
||||
const seriesList = apiResponse?.data?.result || [];
|
||||
const timestampArr = getXAxisTimestamps(seriesList);
|
||||
@ -74,5 +91,10 @@ export const getUPlotChartData = (
|
||||
fillSpans || false,
|
||||
);
|
||||
|
||||
return [timestampArr, ...yAxisValuesArr];
|
||||
return [
|
||||
timestampArr,
|
||||
...(stackedBarChart && isUndefined(hiddenGraph)
|
||||
? getStackedSeries(yAxisValuesArr)
|
||||
: yAxisValuesArr),
|
||||
];
|
||||
};
|
||||
|
@ -3,9 +3,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GetSeriesProps } from '../../getSeriesData';
|
||||
|
||||
export const seriesBarChartData = {
|
||||
apiResponse: {
|
||||
data: {
|
||||
result: [
|
||||
series: [
|
||||
{
|
||||
metric: {},
|
||||
values: [
|
||||
@ -89,274 +87,7 @@ export const seriesBarChartData = {
|
||||
legend: 'forthLength',
|
||||
},
|
||||
],
|
||||
resultType: '',
|
||||
newResult: {
|
||||
status: 'success',
|
||||
data: {
|
||||
resultType: '',
|
||||
result: [
|
||||
{
|
||||
queryName: 'A',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: [],
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '3378',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '3269',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '3341',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '3269',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '3296',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '3280',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '3260',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '3351',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '3345',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '3370',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '3382',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '3249',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683960000,
|
||||
value: '212',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'B',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: [],
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '2878',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '2873',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '2867',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '2837',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '2831',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '2828',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '2773',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '2772',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '2745',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '2732',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '2729',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '2709',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '2706',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'F2',
|
||||
series: [
|
||||
{
|
||||
labels: {
|
||||
F2: 'F2',
|
||||
},
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '504',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '505',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '503',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '514',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '514',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '513',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '524',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '535',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '496',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '537',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '551',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '543',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '-1157',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'F1',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: null,
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '6260',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '6251',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '6237',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '6188',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '6176',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '6169',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '6068',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '6025',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '6042',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '6001',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '5969',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '5955',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '4301',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
widgetMetaData: [
|
||||
{
|
||||
metric: {},
|
||||
@ -446,9 +177,7 @@ export const seriesBarChartData = {
|
||||
} as GetSeriesProps;
|
||||
|
||||
export const seriesLineChartData = {
|
||||
apiResponse: {
|
||||
data: {
|
||||
result: [
|
||||
series: [
|
||||
{
|
||||
metric: {},
|
||||
values: [
|
||||
@ -532,274 +261,7 @@ export const seriesLineChartData = {
|
||||
legend: 'forthLength',
|
||||
},
|
||||
],
|
||||
resultType: '',
|
||||
newResult: {
|
||||
status: 'success',
|
||||
data: {
|
||||
resultType: '',
|
||||
result: [
|
||||
{
|
||||
queryName: 'A',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: [],
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '3378',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '3269',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '3341',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '3269',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '3296',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '3280',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '3260',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '3351',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '3345',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '3370',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '3382',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '3249',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683960000,
|
||||
value: '212',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'B',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: [],
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '2878',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '2873',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '2867',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '2837',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '2831',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '2828',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '2773',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '2772',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '2745',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '2732',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '2729',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '2709',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '2706',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'F2',
|
||||
series: [
|
||||
{
|
||||
labels: {
|
||||
F2: 'F2',
|
||||
},
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '504',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '505',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '503',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '514',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '514',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '513',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '524',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '535',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '496',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '537',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '551',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '543',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '-1157',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
{
|
||||
queryName: 'F1',
|
||||
series: [
|
||||
{
|
||||
labels: {},
|
||||
labelsArray: null,
|
||||
values: [
|
||||
{
|
||||
timestamp: 1708683840000,
|
||||
value: '6260',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683240000,
|
||||
value: '6251',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683780000,
|
||||
value: '6237',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683660000,
|
||||
value: '6188',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683720000,
|
||||
value: '6176',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683360000,
|
||||
value: '6169',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683480000,
|
||||
value: '6068',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683540000,
|
||||
value: '6025',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683300000,
|
||||
value: '6042',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683420000,
|
||||
value: '6001',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683600000,
|
||||
value: '5969',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683900000,
|
||||
value: '5955',
|
||||
},
|
||||
{
|
||||
timestamp: 1708683180000,
|
||||
value: '4301',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
list: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
widgetMetaData: [
|
||||
{
|
||||
metric: {},
|
||||
|
@ -27,6 +27,7 @@ describe('Get Series Data', () => {
|
||||
test('Should return seris drawline line chart for panel type time series', () => {
|
||||
const seriesData = getSeries(seriesLineChartData);
|
||||
// @ts-ignore
|
||||
|
||||
expect(seriesData[1].drawStyle).toBe('line');
|
||||
});
|
||||
});
|
||||
|
@ -97,6 +97,7 @@ export interface IBaseWidget {
|
||||
timePreferance: timePreferenceType;
|
||||
stepSize?: number;
|
||||
yAxisUnit?: string;
|
||||
stackedBarChart?: boolean;
|
||||
bucketCount?: number;
|
||||
bucketWidth?: number;
|
||||
mergeAllActiveQueries?: boolean;
|
||||
|
Loading…
x
Reference in New Issue
Block a user