attempt to set max and min

This commit is contained in:
Nate Kelley 2025-09-24 21:02:24 -06:00
parent 2f035ec70d
commit 84a6af7730
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 88 additions and 10 deletions

View File

@ -7,6 +7,7 @@ import {
DEFAULT_CHART_CONFIG, DEFAULT_CHART_CONFIG,
DEFAULT_COLUMN_LABEL_FORMAT, DEFAULT_COLUMN_LABEL_FORMAT,
} from '@buster/server-shared/metrics'; } from '@buster/server-shared/metrics';
import { fa } from '@faker-js/faker';
import type { import type {
CartesianScaleTypeRegistry, CartesianScaleTypeRegistry,
Scale, Scale,
@ -20,7 +21,7 @@ import type { BusterChartProps } from '../../../BusterChart.types';
import { formatYAxisLabel, yAxisSimilar } from '../../../commonHelpers'; import { formatYAxisLabel, yAxisSimilar } from '../../../commonHelpers';
import { useY2AxisTitle } from './axisHooks/useY2AxisTitle'; import { useY2AxisTitle } from './axisHooks/useY2AxisTitle';
export const DEFAULT_Y2_AXIS_COUNT = 7; export const DEFAULT_Y2_AXIS_COUNT = 9;
export const useY2Axis = ({ export const useY2Axis = ({
columnLabelFormats, columnLabelFormats,
@ -47,6 +48,7 @@ export const useY2Axis = ({
const selectedAxis = selectedAxisProp as ComboChartAxis; const selectedAxis = selectedAxisProp as ComboChartAxis;
const y2AxisKeys = selectedAxis.y2 || []; const y2AxisKeys = selectedAxis.y2 || [];
const yAxisMinValue = yAxis?.min; const yAxisMinValue = yAxis?.min;
const yAxisMaxValue = yAxis?.max;
const y2AxisKeysString = useMemo(() => { const y2AxisKeysString = useMemo(() => {
return y2AxisKeys.join(','); return y2AxisKeys.join(',');
@ -116,6 +118,7 @@ export const useY2Axis = ({
includeBounds: true, includeBounds: true,
}, },
min: yAxisMinValue, min: yAxisMinValue,
max: yAxisMaxValue,
grid: { grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up drawOnChartArea: false, // only want the grid lines for one axis to show up
}, },

View File

@ -1,11 +1,14 @@
/** biome-ignore-all lint/style/noNonNullAssertion: false positive */ /** biome-ignore-all lint/style/noNonNullAssertion: false positive */
import round from 'lodash/round';
import { useMemo } from 'react'; import { useMemo } from 'react';
import type { BusterChartProps } from '../../../BusterChart.types'; import type { BusterChartProps } from '../../../BusterChart.types';
const MIN_PERCENT_DIFFERENCE = 1.5; const MIN_PERCENT_DIFFERENCE = 2;
const MAX_PERCENT_DIFFERENCE = 2; const MAX_PERCENT_DIFFERENCE = 2;
const MIN_OFFSET = 1.1; const MIN_OFFSET = 1.1;
const MAX_OFFSET = 1.1; const MAX_OFFSET = 1.1;
const PERCENTAGE_STEP = 5;
export const useYTickValues = ({ export const useYTickValues = ({
hasY2Axis, hasY2Axis,
@ -25,23 +28,34 @@ export const useYTickValues = ({
const shouldUseMinAndMaxValues = hasY2Axis && columnMetadata && selectedChartType === 'combo'; const shouldUseMinAndMaxValues = hasY2Axis && columnMetadata && selectedChartType === 'combo';
const checkValues = useMemo(() => { const checkValues = useMemo(() => {
if (!shouldUseMinAndMaxValues) return [];
return [...yAxisKeys, ...y2AxisKeys]; return [...yAxisKeys, ...y2AxisKeys];
}, [yAxisKeys, y2AxisKeys]); }, [yAxisKeys, y2AxisKeys, shouldUseMinAndMaxValues]);
const hasOnePercentageValue = useMemo(() => {
if (!shouldUseMinAndMaxValues) return false;
return checkValues.some((key) => {
const columnFormat = columnLabelFormats[key];
return columnFormat?.style === 'percent';
});
}, [checkValues, columnLabelFormats, shouldUseMinAndMaxValues]);
const allYValuesArePercentage = useMemo(() => { const allYValuesArePercentage = useMemo(() => {
if (!shouldUseMinAndMaxValues) return false;
return checkValues.every((key) => { return checkValues.every((key) => {
const columnFormat = columnLabelFormats[key]; const columnFormat = columnLabelFormats[key];
return columnFormat?.style === 'percent'; return columnFormat?.style === 'percent';
}); });
}, [checkValues, columnLabelFormats]); }, [checkValues, columnLabelFormats, shouldUseMinAndMaxValues]);
const columnMap = useMemo(() => { const columnMap = useMemo(() => {
if (!columnMetadata) return new Map(); if (!columnMetadata || !shouldUseMinAndMaxValues) return new Map();
return new Map(columnMetadata.map((col) => [col.name, col])); return new Map(columnMetadata.map((col) => [col.name, col]));
}, [columnMetadata]); }, [columnMetadata, shouldUseMinAndMaxValues]);
// Calculate min/max ranges for y-axis columns // Calculate min/max ranges for y-axis columns
const yAxisRange = useMemo(() => { const yAxisRange = useMemo(() => {
if (!shouldUseMinAndMaxValues) return { min: Infinity, max: -Infinity };
return yAxisKeys.reduce( return yAxisKeys.reduce(
(acc, key) => { (acc, key) => {
const column = columnMap.get(key); const column = columnMap.get(key);
@ -55,10 +69,11 @@ export const useYTickValues = ({
}, },
{ min: Infinity, max: -Infinity } { min: Infinity, max: -Infinity }
); );
}, [yAxisKeys, columnMap]); }, [yAxisKeys, columnMap, shouldUseMinAndMaxValues]);
// Calculate min/max ranges for y2-axis columns // Calculate min/max ranges for y2-axis columns
const y2AxisRange = useMemo(() => { const y2AxisRange = useMemo(() => {
if (!shouldUseMinAndMaxValues) return { min: Infinity, max: -Infinity };
return y2AxisKeys.reduce( return y2AxisKeys.reduce(
(acc, key) => { (acc, key) => {
const column = columnMap.get(key); const column = columnMap.get(key);
@ -72,7 +87,7 @@ export const useYTickValues = ({
}, },
{ min: Infinity, max: -Infinity } { min: Infinity, max: -Infinity }
); );
}, [y2AxisKeys, columnMap]); }, [y2AxisKeys, columnMap, shouldUseMinAndMaxValues]);
const minTickValue: number | undefined = useMemo(() => { const minTickValue: number | undefined = useMemo(() => {
if (!shouldUseMinAndMaxValues) return undefined; if (!shouldUseMinAndMaxValues) return undefined;
@ -110,9 +125,16 @@ export const useYTickValues = ({
// If both min and max values are similar, use the lowest min value // If both min and max values are similar, use the lowest min value
if (minValuesAreSimilar && maxValuesAreSimilar) { if (minValuesAreSimilar && maxValuesAreSimilar) {
return Math.min(yAxisRange.min, y2AxisRange.min) * MIN_OFFSET; if (hasOnePercentageValue) {
// If there's at least one percentage value, round the min to the nearest lower multiple of 5
const min = Math.min(yAxisRange.min, y2AxisRange.min) * MIN_OFFSET;
return Math.floor(min / PERCENTAGE_STEP) * PERCENTAGE_STEP;
}
return round(Math.min(yAxisRange.min, y2AxisRange.min) * MIN_OFFSET, 0);
} }
}, [ }, [
hasOnePercentageValue,
columnLabelFormats, columnLabelFormats,
yAxisRange, yAxisRange,
y2AxisRange, y2AxisRange,
@ -122,7 +144,60 @@ export const useYTickValues = ({
allYValuesArePercentage, allYValuesArePercentage,
]); ]);
const maxTickValue: number | undefined = undefined; const maxTickValue: number | undefined = useMemo(() => {
if (!shouldUseMinAndMaxValues) return undefined;
// If all Y values are percentages, return the highest value
if (allYValuesArePercentage) {
const highestValue = checkValues.reduce((max, key) => {
const column = columnMap.get(key);
return Math.max(max, Number(column?.max_value ?? 0));
}, -Infinity);
if (highestValue === -Infinity) return undefined;
if (highestValue < 1) return 1;
return highestValue;
}
// Reset infinities if no valid data found
if (yAxisRange.min === Infinity) yAxisRange.min = 0;
if (yAxisRange.max === -Infinity) yAxisRange.max = 0;
if (y2AxisRange.min === Infinity) y2AxisRange.min = 0;
if (y2AxisRange.max === -Infinity) y2AxisRange.max = 0;
// Check if min values are within 150% of each other
const minValuesAreSimilar =
Math.abs(yAxisRange.min) > 0 && Math.abs(y2AxisRange.min) > 0
? Math.max(yAxisRange.min, y2AxisRange.min) / Math.min(yAxisRange.min, y2AxisRange.min) <=
MIN_PERCENT_DIFFERENCE
: yAxisRange.min === y2AxisRange.min;
// Check if max values are within 200% of each other
const maxValuesAreSimilar =
Math.abs(yAxisRange.max) > 0 && Math.abs(y2AxisRange.max) > 0
? Math.max(yAxisRange.max, y2AxisRange.max) / Math.min(yAxisRange.max, y2AxisRange.max) <=
MAX_PERCENT_DIFFERENCE
: yAxisRange.max === y2AxisRange.max;
// If both min and max values are similar, use the highest max value
if (minValuesAreSimilar && maxValuesAreSimilar) {
if (hasOnePercentageValue) {
// If there's at least one percentage value, round the max to the nearest higher multiple of 5
const max = Math.max(yAxisRange.max, y2AxisRange.max) * MAX_OFFSET;
return Math.ceil(max / PERCENTAGE_STEP) * PERCENTAGE_STEP;
}
return round(Math.max(yAxisRange.max, y2AxisRange.max) * MAX_OFFSET, 0);
}
}, [
hasOnePercentageValue,
columnLabelFormats,
yAxisRange,
y2AxisRange,
shouldUseMinAndMaxValues,
columnMap,
checkValues,
allYValuesArePercentage,
]);
return { return {
minTickValue, minTickValue,