trendline max and mins

This commit is contained in:
Nate Kelley 2025-05-07 16:42:23 -06:00
parent e9a6c26415
commit 6f44c99e2d
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 57 additions and 37 deletions

View File

@ -22,4 +22,5 @@ export interface Trendline {
| 'median'; //default is linear trend
trendLineColor?: string | null; //OPTIONAL: default is #000000
columnId: string;
id?: string;
}

View File

@ -24,7 +24,8 @@ describe('useTrendlines', () => {
trendlines: [] as TrendlineDataset[],
columnLabelFormats: {} as Record<string, IColumnLabelFormat | undefined>,
selectedChartType: 'line' as ChartType,
lineGroupType: null
lineGroupType: null,
barGroupType: null
};
it('returns the expected structure', () => {
@ -153,6 +154,7 @@ describe('useTrendlines', () => {
const columnId = 'col1';
const props = {
...defaultProps,
trendlines: [
mockTrendlineDataset({
id: 'test-linear-slope',

View File

@ -282,14 +282,14 @@ export const trendlineDatasetCreator: Record<
},
average: (trendline, datasetsWithTicks) => {
const datasets = datasetsWithTicks.datasets;
const selectedDataset = datasets.find((dataset) => dataset.id === trendline.columnId);
const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks(
datasetsWithTicks.datasets,
trendline
);
if (!selectedDataset?.data || selectedDataset.data.length === 0) return [];
// Filter out null/undefined values
const validData = selectedDataset.data.filter((value) => value !== null && value !== undefined);
if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return [];
// Sum all valid values and divide by the count
if (validData.length === 0) return [];
// Sum all valid values and divide by the count
@ -313,14 +313,12 @@ export const trendlineDatasetCreator: Record<
},
min: (trendline, datasetsWithTicks) => {
const datasets = datasetsWithTicks.datasets;
const selectedDataset = datasets.find((dataset) => dataset.id === trendline.columnId);
const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks(
datasetsWithTicks.datasets,
trendline
);
if (!selectedDataset?.data || selectedDataset.data.length === 0) return [];
// Filter out null/undefined values
const validData = selectedDataset.data.filter((value) => value !== null && value !== undefined);
if (validData.length === 0) return [];
if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return [];
// Use the first valid value as initial accumulator
const min = validData.reduce<number>((acc, datapoint) => {
@ -341,14 +339,12 @@ export const trendlineDatasetCreator: Record<
},
max: (trendline, datasetsWithTicks) => {
const datasets = datasetsWithTicks.datasets;
const selectedDataset = datasets.find((dataset) => dataset.id === trendline.columnId);
const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks(
datasetsWithTicks.datasets,
trendline
);
if (!selectedDataset?.data || selectedDataset.data.length === 0) return [];
// Filter out null/undefined values
const validData = selectedDataset.data.filter((value) => value !== null && value !== undefined);
if (validData.length === 0) return [];
if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return [];
// Use the first valid value as initial accumulator
const max = validData.reduce<number>((acc, datapoint) => {
@ -369,15 +365,15 @@ export const trendlineDatasetCreator: Record<
},
median: (trendline, datasetsWithTicks) => {
const datasets = datasetsWithTicks.datasets;
const selectedDataset = datasets.find((dataset) => dataset.id === trendline.columnId);
const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks(
datasetsWithTicks.datasets,
trendline
);
if (!selectedDataset?.data || selectedDataset.data.length === 0) return [];
if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return [];
// Sort the data and get the middle value
const sortedData = [...selectedDataset.data]
.filter((value) => value !== null && value !== undefined)
.sort((a, b) => (a as number) - (b as number));
const sortedData = [...validData].sort((a, b) => (a as number) - (b as number));
let median: number;
const midIndex = Math.floor(sortedData.length / 2);

View File

@ -42,8 +42,9 @@ export const useDataTrendlineOptions = ({
!hasTrendlines ||
!datasetOptions ||
!datasetOptions.datasets.length
)
) {
return [] as TrendlineDataset[];
}
const trendlineDatasets: TrendlineDataset[] = [];

View File

@ -18,6 +18,7 @@ import { EditTrendlineShowLine } from './EditTrendlineShowLine';
import { EditTrendlineOption } from './EditTrendlineOption';
import { TypeToLabel } from './config';
import { JOIN_CHARACTER } from '@/components/ui/charts/commonHelpers';
import isEqual from 'lodash/isEqual';
export interface LoopTrendline extends Trendline {
id: string;
@ -40,7 +41,7 @@ export const EditTrendline: React.FC<{
selectedChartType
}) => {
const [trends, _setTrends] = useState<LoopTrendline[]>(
trendlines.map((trend) => ({ ...trend, id: uuidv4() }))
trendlines.map((trend) => ({ ...trend, id: trend.id || uuidv4() }))
);
const [newTrendIds, { add: addNewTrendId }] = useSet<string>();
@ -60,13 +61,30 @@ export const EditTrendline: React.FC<{
}, []);
const onAddTrendline = useMemoizedFn(() => {
const getNewType = () => {
const types = [
'linear_regression',
'polynomial_regression',
'exponential_regression',
'logarithmic_regression',
'average',
'min',
'max',
'median'
] as const;
return types[Math.floor(Math.random() * types.length)];
};
const hasLinearRegression = trends.some((trend) => trend.type === 'linear_regression');
const type = hasLinearRegression ? getNewType() : ('linear_regression' as const);
const newTrendline: Required<LoopTrendline> = {
id: uuidv4(),
show: true,
showTrendlineLabel: false,
trendlineLabel: null,
type: 'linear_regression',
trendLineColor: null,
type,
trendLineColor: '#FF0000',
columnId: selectedAxis.y[0] || ''
};
@ -77,12 +95,8 @@ export const EditTrendline: React.FC<{
});
const onUpdateTrendlines = useMemoizedFn((trends: LoopTrendline[]) => {
const newTrends = trends.map(({ id, ...rest }) => ({
...rest
}));
setTimeout(() => {
onUpdateChartConfig({ trendlines: newTrends });
onUpdateChartConfig({ trendlines: trends });
}, 30);
});
@ -120,7 +134,13 @@ export const EditTrendline: React.FC<{
};
}, [trends]);
//TODO: fix the bug where we need to "reset" the trends when the reset button is clicked
useEffect(() => {
const updatedTrends = trendlines.map((trend) => ({ ...trend, id: trend.id || uuidv4() }));
if (!isEqual(updatedTrends, trends)) {
_setTrends(updatedTrends);
}
}, [trendlines]);
return (
<div className="flex flex-col space-y-2.5">