Compare commits

...

7 Commits

Author SHA1 Message Date
Abhi kumar
f11153cdec Merge branch 'main' into chore/uplot-builder-restructure 2026-02-28 18:55:41 +05:30
Abhi Kumar
a380c4d6be chore: fixed tsc + test 2026-02-28 18:08:39 +05:30
Abhi Kumar
a0f576e5fb chore: fixed tsc + test 2026-02-28 17:53:44 +05:30
Abhi Kumar
98013ee3b9 chore: fixed tsc + test 2026-02-28 15:51:58 +05:30
Abhi Kumar
97b94fc4f5 chore: updated timezone types 2026-02-28 15:20:24 +05:30
Abhi Kumar
0f3f49b96a chore: updated baseconfigbuilder test 2026-02-28 15:15:21 +05:30
Abhi Kumar
0928a30863 chore: made baseconfigbuilder generic to be used across different charts 2026-02-28 15:10:16 +05:30
16 changed files with 108 additions and 82 deletions

View File

@@ -1,3 +1,4 @@
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PrecisionOption } from 'components/Graph/types'; import { PrecisionOption } from 'components/Graph/types';
import { LegendConfig, TooltipRenderArgs } from 'lib/uPlotV2/components/types'; import { LegendConfig, TooltipRenderArgs } from 'lib/uPlotV2/components/types';
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder'; import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
@@ -8,7 +9,7 @@ interface BaseChartProps {
height: number; height: number;
showTooltip?: boolean; showTooltip?: boolean;
showLegend?: boolean; showLegend?: boolean;
timezone: string; timezone?: Timezone;
canPinTooltip?: boolean; canPinTooltip?: boolean;
yAxisUnit?: string; yAxisUnit?: string;
decimalPrecision?: PrecisionOption; decimalPrecision?: PrecisionOption;

View File

@@ -129,12 +129,12 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
onDestroy={onPlotDestroy} onDestroy={onPlotDestroy}
yAxisUnit={widget.yAxisUnit} yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision} decimalPrecision={widget.decimalPrecision}
timezone={timezone.value}
data={chartData as uPlot.AlignedData} data={chartData as uPlot.AlignedData}
width={containerDimensions.width} width={containerDimensions.width}
height={containerDimensions.height} height={containerDimensions.height}
layoutChildren={layoutChildren} layoutChildren={layoutChildren}
isStackedBarChart={widget.stackedBarChart ?? false} isStackedBarChart={widget.stackedBarChart ?? false}
timezone={timezone}
> >
<ContextMenu <ContextMenu
coordinates={coordinates} coordinates={coordinates}

View File

@@ -5,12 +5,7 @@ import { getInitialStackedBands } from 'container/DashboardContainer/visualizati
import { getLegend } from 'lib/dashboard/getQueryResults'; import { getLegend } from 'lib/dashboard/getQueryResults';
import getLabelName from 'lib/getLabelName'; import getLabelName from 'lib/getLabelName';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { import { DrawStyle } from 'lib/uPlotV2/config/types';
DrawStyle,
LineInterpolation,
LineStyle,
VisibilityMode,
} from 'lib/uPlotV2/config/types';
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder'; import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
import { get } from 'lodash-es'; import { get } from 'lodash-es';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
@@ -63,7 +58,12 @@ export function prepareBarPanelConfig({
const minStepInterval = Math.min(...Object.values(stepIntervals)); const minStepInterval = Math.min(...Object.values(stepIntervals));
const builder = buildBaseConfig({ const builder = buildBaseConfig({
widget, widgetId: widget.id,
thresholds: widget.thresholds,
yAxisUnit: widget.yAxisUnit,
softMin: widget.softMin ?? undefined,
softMax: widget.softMax ?? undefined,
isLogScale: widget.isLogScale,
isDarkMode, isDarkMode,
onClick, onClick,
onDragSelect, onDragSelect,
@@ -98,14 +98,8 @@ export function prepareBarPanelConfig({
builder.addSeries({ builder.addSeries({
scaleKey: 'y', scaleKey: 'y',
drawStyle: DrawStyle.Bar, drawStyle: DrawStyle.Bar,
panelType: PANEL_TYPES.BAR,
label: label, label: label,
colorMapping: widget.customLegendColors ?? {}, colorMapping: widget.customLegendColors ?? {},
spanGaps: false,
lineStyle: LineStyle.Solid,
lineInterpolation: LineInterpolation.Spline,
showPoints: VisibilityMode.Never,
pointSize: 5,
isDarkMode, isDarkMode,
stepInterval: currentStepInterval, stepInterval: currentStepInterval,
}); });

View File

@@ -100,7 +100,7 @@ function HistogramPanel(props: PanelWrapperProps): JSX.Element {
yAxisUnit={widget.yAxisUnit} yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision} decimalPrecision={widget.decimalPrecision}
syncMode={DashboardCursorSync.Crosshair} syncMode={DashboardCursorSync.Crosshair}
timezone={timezone.value} timezone={timezone}
data={chartData as uPlot.AlignedData} data={chartData as uPlot.AlignedData}
width={containerDimensions.width} width={containerDimensions.width}
height={containerDimensions.height} height={containerDimensions.height}

View File

@@ -154,7 +154,12 @@ export function prepareHistogramPanelConfig({
isDarkMode: boolean; isDarkMode: boolean;
}): UPlotConfigBuilder { }): UPlotConfigBuilder {
const builder = buildBaseConfig({ const builder = buildBaseConfig({
widget, widgetId: widget.id,
thresholds: widget.thresholds,
yAxisUnit: widget.yAxisUnit,
softMin: widget.softMin ?? undefined,
softMax: widget.softMax ?? undefined,
isLogScale: widget.isLogScale,
isDarkMode, isDarkMode,
apiResponse, apiResponse,
panelMode, panelMode,
@@ -191,10 +196,8 @@ export function prepareHistogramPanelConfig({
builder.addSeries({ builder.addSeries({
label: '', label: '',
scaleKey: 'y', scaleKey: 'y',
drawStyle: DrawStyle.Bar, drawStyle: DrawStyle.Histogram,
panelType: PANEL_TYPES.HISTOGRAM,
colorMapping: widget.customLegendColors ?? {}, colorMapping: widget.customLegendColors ?? {},
spanGaps: false,
barWidthFactor: 1, barWidthFactor: 1,
pointSize: 5, pointSize: 5,
lineColor: '#3f5ecc', lineColor: '#3f5ecc',
@@ -216,10 +219,8 @@ export function prepareHistogramPanelConfig({
builder.addSeries({ builder.addSeries({
label: label, label: label,
scaleKey: 'y', scaleKey: 'y',
drawStyle: DrawStyle.Bar, drawStyle: DrawStyle.Histogram,
panelType: PANEL_TYPES.HISTOGRAM,
colorMapping: widget.customLegendColors ?? {}, colorMapping: widget.customLegendColors ?? {},
spanGaps: false,
barWidthFactor: 1, barWidthFactor: 1,
pointSize: 5, pointSize: 5,
isDarkMode, isDarkMode,

View File

@@ -118,7 +118,7 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
}} }}
yAxisUnit={widget.yAxisUnit} yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision} decimalPrecision={widget.decimalPrecision}
timezone={timezone.value} timezone={timezone}
data={chartData as uPlot.AlignedData} data={chartData as uPlot.AlignedData}
width={containerDimensions.width} width={containerDimensions.width}
height={containerDimensions.height} height={containerDimensions.height}

View File

@@ -82,7 +82,12 @@ export const prepareUPlotConfig = ({
const minStepInterval = Math.min(...Object.values(stepIntervals)); const minStepInterval = Math.min(...Object.values(stepIntervals));
const builder = buildBaseConfig({ const builder = buildBaseConfig({
widget, widgetId: widget.id,
thresholds: widget.thresholds,
yAxisUnit: widget.yAxisUnit,
softMin: widget.softMin ?? undefined,
softMax: widget.softMax ?? undefined,
isLogScale: widget.isLogScale,
isDarkMode, isDarkMode,
onClick, onClick,
onDragSelect, onDragSelect,
@@ -120,7 +125,6 @@ export const prepareUPlotConfig = ({
: VisibilityMode.Never, : VisibilityMode.Never,
pointSize: 5, pointSize: 5,
isDarkMode, isDarkMode,
panelType: PANEL_TYPES.TIME_SERIES,
}); });
}); });

View File

@@ -1,11 +1,11 @@
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
import { STEP_INTERVAL_MULTIPLIER } from 'lib/uPlotV2/constants'; import { STEP_INTERVAL_MULTIPLIER } from 'lib/uPlotV2/constants';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import uPlot from 'uplot'; import uPlot from 'uplot';
import { PanelMode } from '../../types'; import { PanelMode } from '../../types';
import { buildBaseConfig } from '../baseConfigBuilder'; import { BaseConfigBuilderProps, buildBaseConfig } from '../baseConfigBuilder';
jest.mock( jest.mock(
'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils', 'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils',
@@ -27,16 +27,17 @@ jest.mock('lib/uPlotLib/plugins/onClickPlugin', () => ({
default: jest.fn().mockReturnValue({ name: 'onClickPlugin' }), default: jest.fn().mockReturnValue({ name: 'onClickPlugin' }),
})); }));
const createWidget = (overrides: Partial<Widgets> = {}): Widgets => const createBaseConfigBuilderProps = (
({ overrides: Partial<BaseConfigBuilderProps> = {},
id: 'widget-1', ): Partial<BaseConfigBuilderProps> => ({
yAxisUnit: 'ms', widgetId: 'widget-1',
isLogScale: false, yAxisUnit: 'ms',
softMin: undefined, isLogScale: false,
softMax: undefined, softMin: undefined,
thresholds: [], softMax: undefined,
...overrides, thresholds: [],
} as Widgets); ...overrides,
});
const createApiResponse = ( const createApiResponse = (
overrides: Partial<MetricRangePayloadProps> = {}, overrides: Partial<MetricRangePayloadProps> = {},
@@ -47,7 +48,7 @@ const createApiResponse = (
} as MetricRangePayloadProps); } as MetricRangePayloadProps);
const baseProps = { const baseProps = {
widget: createWidget(), ...createBaseConfigBuilderProps(),
apiResponse: createApiResponse(), apiResponse: createApiResponse(),
isDarkMode: true, isDarkMode: true,
panelMode: PanelMode.DASHBOARD_VIEW, panelMode: PanelMode.DASHBOARD_VIEW,
@@ -67,7 +68,7 @@ describe('buildBaseConfig', () => {
const builder = buildBaseConfig({ const builder = buildBaseConfig({
...baseProps, ...baseProps,
panelMode: PanelMode.DASHBOARD_VIEW, panelMode: PanelMode.DASHBOARD_VIEW,
widget: createWidget({ id: 'my-widget' }), ...createBaseConfigBuilderProps({ widgetId: 'my-widget' }),
}); });
expect(builder.getWidgetId()).toBe('my-widget'); expect(builder.getWidgetId()).toBe('my-widget');
@@ -127,7 +128,7 @@ describe('buildBaseConfig', () => {
it('configures log scale on y axis when widget.isLogScale is true', () => { it('configures log scale on y axis when widget.isLogScale is true', () => {
const builder = buildBaseConfig({ const builder = buildBaseConfig({
...baseProps, ...baseProps,
widget: createWidget({ isLogScale: true }), ...createBaseConfigBuilderProps({ isLogScale: true }),
}); });
const config = builder.getConfig(); const config = builder.getConfig();
@@ -171,7 +172,7 @@ describe('buildBaseConfig', () => {
it('adds thresholds from widget', () => { it('adds thresholds from widget', () => {
const builder = buildBaseConfig({ const builder = buildBaseConfig({
...baseProps, ...baseProps,
widget: createWidget({ ...createBaseConfigBuilderProps({
thresholds: [ thresholds: [
{ {
thresholdValue: 80, thresholdValue: 80,
@@ -179,7 +180,7 @@ describe('buildBaseConfig', () => {
thresholdUnit: 'ms', thresholdUnit: 'ms',
thresholdLabel: 'High', thresholdLabel: 'High',
}, },
] as Widgets['thresholds'], ] as ThresholdProps[],
}), }),
}); });

View File

@@ -1,5 +1,6 @@
import { Timezone } from 'components/CustomTimePicker/timezoneUtils'; import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
import onClickPlugin, { import onClickPlugin, {
OnClickPluginOpts, OnClickPluginOpts,
} from 'lib/uPlotLib/plugins/onClickPlugin'; } from 'lib/uPlotLib/plugins/onClickPlugin';
@@ -9,28 +10,32 @@ import {
} from 'lib/uPlotV2/config/types'; } from 'lib/uPlotV2/config/types';
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder'; import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
import { ThresholdsDrawHookOptions } from 'lib/uPlotV2/hooks/types'; import { ThresholdsDrawHookOptions } from 'lib/uPlotV2/hooks/types';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import uPlot from 'uplot'; import uPlot from 'uplot';
import { PanelMode } from '../types'; import { PanelMode } from '../types';
export interface BaseConfigBuilderProps { export interface BaseConfigBuilderProps {
widget: Widgets; widgetId?: string;
thresholds?: ThresholdProps[];
apiResponse: MetricRangePayloadProps; apiResponse: MetricRangePayloadProps;
isDarkMode: boolean; isDarkMode: boolean;
onClick?: OnClickPluginOpts['onClick']; onClick?: OnClickPluginOpts['onClick'];
onDragSelect?: (startTime: number, endTime: number) => void; onDragSelect?: (startTime: number, endTime: number) => void;
timezone?: Timezone; timezone?: Timezone;
panelMode: PanelMode; panelMode?: PanelMode;
panelType: PANEL_TYPES; panelType: PANEL_TYPES;
minTimeScale?: number; minTimeScale?: number;
maxTimeScale?: number; maxTimeScale?: number;
stepInterval?: number; stepInterval?: number;
isLogScale?: boolean;
yAxisUnit?: string;
softMin?: number;
softMax?: number;
} }
export function buildBaseConfig({ export function buildBaseConfig({
widget, widgetId,
isDarkMode, isDarkMode,
onClick, onClick,
onDragSelect, onDragSelect,
@@ -38,9 +43,14 @@ export function buildBaseConfig({
timezone, timezone,
panelMode, panelMode,
panelType, panelType,
thresholds,
minTimeScale, minTimeScale,
maxTimeScale, maxTimeScale,
stepInterval, stepInterval,
isLogScale,
yAxisUnit,
softMin,
softMax,
}: BaseConfigBuilderProps): UPlotConfigBuilder { }: BaseConfigBuilderProps): UPlotConfigBuilder {
const tzDate = timezone const tzDate = timezone
? (timestamp: number): Date => ? (timestamp: number): Date =>
@@ -49,27 +59,26 @@ export function buildBaseConfig({
const builder = new UPlotConfigBuilder({ const builder = new UPlotConfigBuilder({
onDragSelect, onDragSelect,
widgetId: widget.id, widgetId: widgetId,
tzDate, tzDate,
shouldSaveSelectionPreference: panelMode === PanelMode.DASHBOARD_VIEW, shouldSaveSelectionPreference: panelMode === PanelMode.DASHBOARD_VIEW,
selectionPreferencesSource: [ selectionPreferencesSource: panelMode
PanelMode.DASHBOARD_VIEW, ? [PanelMode.DASHBOARD_VIEW, PanelMode.STANDALONE_VIEW].includes(panelMode)
PanelMode.STANDALONE_VIEW, ? SelectionPreferencesSource.LOCAL_STORAGE
].includes(panelMode) : SelectionPreferencesSource.IN_MEMORY
? SelectionPreferencesSource.LOCAL_STORAGE
: SelectionPreferencesSource.IN_MEMORY, : SelectionPreferencesSource.IN_MEMORY,
stepInterval, stepInterval,
}); });
const thresholdOptions: ThresholdsDrawHookOptions = { const thresholdOptions: ThresholdsDrawHookOptions = {
scaleKey: 'y', scaleKey: 'y',
thresholds: (widget.thresholds || []).map((threshold) => ({ thresholds: (thresholds || []).map((threshold) => ({
thresholdValue: threshold.thresholdValue ?? 0, thresholdValue: threshold.thresholdValue ?? 0,
thresholdColor: threshold.thresholdColor, thresholdColor: threshold.thresholdColor,
thresholdUnit: threshold.thresholdUnit, thresholdUnit: threshold.thresholdUnit,
thresholdLabel: threshold.thresholdLabel, thresholdLabel: threshold.thresholdLabel,
})), })),
yAxisUnit: widget.yAxisUnit, yAxisUnit: yAxisUnit,
}; };
builder.addThresholds(thresholdOptions); builder.addThresholds(thresholdOptions);
@@ -79,8 +88,8 @@ export function buildBaseConfig({
time: true, time: true,
min: minTimeScale, min: minTimeScale,
max: maxTimeScale, max: maxTimeScale,
logBase: widget.isLogScale ? 10 : undefined, logBase: isLogScale ? 10 : undefined,
distribution: widget.isLogScale distribution: isLogScale
? DistributionType.Logarithmic ? DistributionType.Logarithmic
: DistributionType.Linear, : DistributionType.Linear,
}); });
@@ -91,11 +100,11 @@ export function buildBaseConfig({
time: false, time: false,
min: undefined, min: undefined,
max: undefined, max: undefined,
softMin: widget.softMin ?? undefined, softMin: softMin,
softMax: widget.softMax ?? undefined, softMax: softMax,
thresholds: thresholdOptions, thresholds: thresholdOptions,
logBase: widget.isLogScale ? 10 : undefined, logBase: isLogScale ? 10 : undefined,
distribution: widget.isLogScale distribution: isLogScale
? DistributionType.Logarithmic ? DistributionType.Logarithmic
: DistributionType.Linear, : DistributionType.Linear,
}); });
@@ -114,7 +123,7 @@ export function buildBaseConfig({
show: true, show: true,
side: 2, side: 2,
isDarkMode, isDarkMode,
isLogScale: widget.isLogScale, isLogScale: isLogScale,
panelType, panelType,
}); });
@@ -123,8 +132,8 @@ export function buildBaseConfig({
show: true, show: true,
side: 3, side: 3,
isDarkMode, isDarkMode,
isLogScale: widget.isLogScale, isLogScale: isLogScale,
yAxisUnit: widget.yAxisUnit, yAxisUnit: yAxisUnit,
panelType, panelType,
}); });

View File

@@ -4,6 +4,7 @@ import cx from 'classnames';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import { useTimezone } from 'providers/Timezone';
import { TooltipProps } from '../types'; import { TooltipProps } from '../types';
@@ -22,6 +23,14 @@ export default function Tooltip({
const isDarkMode = useIsDarkMode(); const isDarkMode = useIsDarkMode();
const [listHeight, setListHeight] = useState(0); const [listHeight, setListHeight] = useState(0);
const tooltipContent = content ?? []; const tooltipContent = content ?? [];
const { timezone: userTimezone } = useTimezone();
const resolvedTimezone = useMemo(() => {
if (!timezone) {
return userTimezone.value;
}
return timezone.value;
}, [timezone, userTimezone]);
const headerTitle = useMemo(() => { const headerTitle = useMemo(() => {
if (!showTooltipHeader) { if (!showTooltipHeader) {
@@ -33,10 +42,10 @@ export default function Tooltip({
return null; return null;
} }
return dayjs(data[0][cursorIdx] * 1000) return dayjs(data[0][cursorIdx] * 1000)
.tz(timezone) .tz(resolvedTimezone)
.format(DATE_TIME_FORMATS.MONTH_DATETIME_SECONDS); .format(DATE_TIME_FORMATS.MONTH_DATETIME_SECONDS);
}, [ }, [
timezone, resolvedTimezone,
uPlotInstance.data, uPlotInstance.data,
uPlotInstance.cursor.idx, uPlotInstance.cursor.idx,
showTooltipHeader, showTooltipHeader,

View File

@@ -83,7 +83,7 @@ function createUPlotInstance(cursorIdx: number | null): uPlot {
function renderTooltip(props: Partial<TooltipTestProps> = {}): RenderResult { function renderTooltip(props: Partial<TooltipTestProps> = {}): RenderResult {
const defaultProps: TooltipTestProps = { const defaultProps: TooltipTestProps = {
uPlotInstance: createUPlotInstance(null), uPlotInstance: createUPlotInstance(null),
timezone: 'UTC', timezone: { value: 'UTC', name: 'UTC', offset: '0', searchIndex: '0' },
content: [], content: [],
showTooltipHeader: true, showTooltipHeader: true,
// TooltipRenderArgs (not used directly in component but required by type) // TooltipRenderArgs (not used directly in component but required by type)

View File

@@ -1,4 +1,5 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PrecisionOption } from 'components/Graph/types'; import { PrecisionOption } from 'components/Graph/types';
import uPlot from 'uplot'; import uPlot from 'uplot';
@@ -61,7 +62,7 @@ export interface TooltipRenderArgs {
export interface BaseTooltipProps { export interface BaseTooltipProps {
showTooltipHeader?: boolean; showTooltipHeader?: boolean;
timezone: string; timezone?: Timezone;
yAxisUnit?: string; yAxisUnit?: string;
decimalPrecision?: PrecisionOption; decimalPrecision?: PrecisionOption;
content?: TooltipContentItem[]; content?: TooltipContentItem[];

View File

@@ -1,4 +1,3 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import { themeColors } from 'constants/theme'; import { themeColors } from 'constants/theme';
import { generateColor } from 'lib/uPlotLib/utils/generateColor'; import { generateColor } from 'lib/uPlotLib/utils/generateColor';
import { calculateWidthBasedOnStepInterval } from 'lib/uPlotV2/utils'; import { calculateWidthBasedOnStepInterval } from 'lib/uPlotV2/utils';
@@ -23,6 +22,9 @@ import {
* Path builders are static and shared across all instances of UPlotSeriesBuilder * Path builders are static and shared across all instances of UPlotSeriesBuilder
*/ */
let builders: PathBuilders | null = null; let builders: PathBuilders | null = null;
const DEFAULT_LINE_WIDTH = 2;
export const POINT_SIZE_FACTOR = 2.5;
export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> { export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
constructor(props: SeriesProps) { constructor(props: SeriesProps) {
super(props); super(props);
@@ -53,7 +55,7 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
const { lineWidth, lineStyle, lineCap, fillColor } = this.props; const { lineWidth, lineStyle, lineCap, fillColor } = this.props;
const lineConfig: Partial<Series> = { const lineConfig: Partial<Series> = {
stroke: resolvedLineColor, stroke: resolvedLineColor,
width: lineWidth ?? 2, width: lineWidth ?? DEFAULT_LINE_WIDTH,
}; };
if (lineStyle === LineStyle.Dashed) { if (lineStyle === LineStyle.Dashed) {
@@ -66,9 +68,9 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
if (fillColor) { if (fillColor) {
lineConfig.fill = fillColor; lineConfig.fill = fillColor;
} else if (this.props.panelType === PANEL_TYPES.BAR) { } else if (this.props.drawStyle === DrawStyle.Bar) {
lineConfig.fill = resolvedLineColor; lineConfig.fill = resolvedLineColor;
} else if (this.props.panelType === PANEL_TYPES.HISTOGRAM) { } else if (this.props.drawStyle === DrawStyle.Histogram) {
lineConfig.fill = `${resolvedLineColor}40`; lineConfig.fill = `${resolvedLineColor}40`;
} }
@@ -137,10 +139,19 @@ export class UPlotSeriesBuilder extends ConfigBuilder<SeriesProps, Series> {
drawStyle, drawStyle,
showPoints, showPoints,
} = this.props; } = this.props;
/**
* If pointSize is not provided, use the lineWidth * POINT_SIZE_FACTOR
* to determine the point size.
* POINT_SIZE_FACTOR is 2, so the point size will be 2x the line width.
*/
const resolvedPointSize =
pointSize ?? (lineWidth ?? DEFAULT_LINE_WIDTH) * POINT_SIZE_FACTOR;
const pointsConfig: Partial<Series.Points> = { const pointsConfig: Partial<Series.Points> = {
stroke: resolvedLineColor, stroke: resolvedLineColor,
fill: resolvedLineColor, fill: resolvedLineColor,
size: !pointSize || pointSize < (lineWidth ?? 2) ? undefined : pointSize, size: resolvedPointSize,
filter: pointsFilter || undefined, filter: pointsFilter || undefined,
}; };
@@ -231,7 +242,7 @@ function getPathBuilder({
throw new Error('Required uPlot path builders are not available'); throw new Error('Required uPlot path builders are not available');
} }
if (drawStyle === DrawStyle.Bar) { if (drawStyle === DrawStyle.Bar || drawStyle === DrawStyle.Histogram) {
const pathBuilders = uPlot.paths; const pathBuilders = uPlot.paths;
return getBarPathBuilder({ return getBarPathBuilder({
pathBuilders, pathBuilders,

View File

@@ -1,4 +1,3 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import uPlot from 'uplot'; import uPlot from 'uplot';
import { import {
@@ -43,7 +42,6 @@ describe('UPlotConfigBuilder', () => {
label: 'Requests', label: 'Requests',
colorMapping: {}, colorMapping: {},
drawStyle: DrawStyle.Line, drawStyle: DrawStyle.Line,
panelType: PANEL_TYPES.TIME_SERIES,
...overrides, ...overrides,
}); });

View File

@@ -1,4 +1,3 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import { themeColors } from 'constants/theme'; import { themeColors } from 'constants/theme';
import uPlot from 'uplot'; import uPlot from 'uplot';
@@ -9,7 +8,7 @@ import {
LineStyle, LineStyle,
VisibilityMode, VisibilityMode,
} from '../types'; } from '../types';
import { UPlotSeriesBuilder } from '../UPlotSeriesBuilder'; import { POINT_SIZE_FACTOR, UPlotSeriesBuilder } from '../UPlotSeriesBuilder';
const createBaseProps = ( const createBaseProps = (
overrides: Partial<SeriesProps> = {}, overrides: Partial<SeriesProps> = {},
@@ -19,7 +18,6 @@ const createBaseProps = (
colorMapping: {}, colorMapping: {},
drawStyle: DrawStyle.Line, drawStyle: DrawStyle.Line,
isDarkMode: false, isDarkMode: false,
panelType: PANEL_TYPES.TIME_SERIES,
...overrides, ...overrides,
}); });
@@ -137,7 +135,6 @@ describe('UPlotSeriesBuilder', () => {
const smallPointsBuilder = new UPlotSeriesBuilder( const smallPointsBuilder = new UPlotSeriesBuilder(
createBaseProps({ createBaseProps({
lineWidth: 4, lineWidth: 4,
pointSize: 2,
}), }),
); );
const largePointsBuilder = new UPlotSeriesBuilder( const largePointsBuilder = new UPlotSeriesBuilder(
@@ -150,7 +147,7 @@ describe('UPlotSeriesBuilder', () => {
const smallConfig = smallPointsBuilder.getConfig(); const smallConfig = smallPointsBuilder.getConfig();
const largeConfig = largePointsBuilder.getConfig(); const largeConfig = largePointsBuilder.getConfig();
expect(smallConfig.points?.size).toBeUndefined(); expect(smallConfig.points?.size).toBe(4 * POINT_SIZE_FACTOR); // should be lineWidth * POINT_SIZE_FACTOR, when pointSize is not provided
expect(largeConfig.points?.size).toBe(4); expect(largeConfig.points?.size).toBe(4);
}); });

View File

@@ -112,6 +112,7 @@ export enum DrawStyle {
Line = 'line', Line = 'line',
Points = 'points', Points = 'points',
Bar = 'bar', Bar = 'bar',
Histogram = 'histogram',
} }
export enum LineInterpolation { export enum LineInterpolation {
@@ -168,7 +169,6 @@ export interface PointsConfig {
export interface SeriesProps extends LineConfig, PointsConfig, BarConfig { export interface SeriesProps extends LineConfig, PointsConfig, BarConfig {
scaleKey: string; scaleKey: string;
label?: string; label?: string;
panelType: PANEL_TYPES;
colorMapping: Record<string, string>; colorMapping: Record<string, string>;
drawStyle: DrawStyle; drawStyle: DrawStyle;
pathBuilder?: Series.PathBuilder; pathBuilder?: Series.PathBuilder;