Skip to content

Commit e35d3fc

Browse files
JCQuintasalexfauquette
authored andcommitted
[chart] Use default values instead of non-null assertion to prevent error being thrown (mui#13637)
Signed-off-by: Jose C Quintas Jr <[email protected]> Co-authored-by: Alexandre Fauquette <[email protected]>
1 parent 06be389 commit e35d3fc

7 files changed

Lines changed: 48 additions & 26 deletions

File tree

packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,34 @@ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) {
8080

8181
const getXPosition = getValueToPositionMapper(xScale);
8282
const getYPosition = getValueToPositionMapper(yScale);
83+
84+
const axisX = axis.x;
85+
const axisY = axis.y;
86+
87+
const isBandScaleX = xAxisHighlight === 'band' && axisX !== null && isBandScale(xScale);
88+
const isBandScaleY = yAxisHighlight === 'band' && axisY !== null && isBandScale(yScale);
89+
90+
if (process.env.NODE_ENV !== 'production') {
91+
const isXError = isBandScaleX && xScale(axisX.value) === undefined;
92+
const isYError = isBandScaleY && yScale(axisY.value) === undefined;
93+
94+
if (isXError || isYError) {
95+
console.error(
96+
[
97+
`MUI X Charts: The position value provided for the axis is not valid for the current scale.`,
98+
`This probably means something is wrong with the data passed to the chart.`,
99+
`The ChartsAxisHighlight component will not be displayed.`,
100+
].join('\n'),
101+
);
102+
}
103+
}
104+
83105
return (
84106
<React.Fragment>
85-
{xAxisHighlight === 'band' && axis.x !== null && isBandScale(xScale) && (
107+
{isBandScaleX && xScale(axisX.value) !== undefined && (
86108
<ChartsAxisHighlightPath
87-
d={`M ${xScale(axis.x.value)! - (xScale.step() - xScale.bandwidth()) / 2} ${
109+
// @ts-expect-error, xScale value is checked in the statement above
110+
d={`M ${xScale(axisX.value) - (xScale.step() - xScale.bandwidth()) / 2} ${
88111
yScale.range()[0]
89112
} l ${xScale.step()} 0 l 0 ${
90113
yScale.range()[1] - yScale.range()[0]
@@ -94,10 +117,11 @@ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) {
94117
/>
95118
)}
96119

97-
{yAxisHighlight === 'band' && axis.y !== null && isBandScale(yScale) && (
120+
{isBandScaleY && yScale(axisY.value) === undefined && (
98121
<ChartsAxisHighlightPath
99122
d={`M ${xScale.range()[0]} ${
100-
yScale(axis.y.value)! - (yScale.step() - yScale.bandwidth()) / 2
123+
// @ts-expect-error, yScale value is checked in the statement above
124+
yScale(axisY.value) - (yScale.step() - yScale.bandwidth()) / 2
101125
} l 0 ${yScale.step()} l ${
102126
xScale.range()[1] - xScale.range()[0]
103127
} 0 l 0 ${-yScale.step()} Z`}

packages/x-charts/src/ChartsVoronoiHandler/ChartsVoronoiHandler.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
55
import { InteractionContext } from '../context/InteractionProvider';
66
import { useCartesianContext } from '../context/CartesianProvider';
77
import { getValueToPositionMapper } from '../hooks/useScale';
8-
import { getSVGPoint } from '../internals/utils';
8+
import { getSVGPoint } from '../internals/getSVGPoint';
99
import { ScatterItemIdentifier } from '../models';
1010
import { SeriesId } from '../models/seriesType/common';
1111
import { useDrawingArea, useSvgRef } from '../hooks';

packages/x-charts/src/hooks/useAxisEvents.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { InteractionContext } from '../context/InteractionProvider';
33
import { useCartesianContext } from '../context/CartesianProvider';
44
import { isBandScale } from '../internals/isBandScale';
55
import { AxisDefaultized } from '../models/axis';
6-
import { getSVGPoint } from '../internals/utils';
6+
import { getSVGPoint } from '../internals/getSVGPoint';
77
import { useSvgRef } from './useSvgRef';
88
import { useDrawingArea } from './useDrawingArea';
99

@@ -31,10 +31,7 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
3131
return () => {};
3232
}
3333

34-
const getUpdate = (axisConfig: AxisDefaultized, mouseValue: number) => {
35-
if (usedXAxis === null) {
36-
return null;
37-
}
34+
function getNewAxisState(axisConfig: AxisDefaultized, mouseValue: number) {
3835
const { scale, data: axisData, reverse } = axisConfig;
3936

4037
if (!isBandScale(scale)) {
@@ -93,7 +90,7 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
9390
index: dataIndex,
9491
value: axisData![dataIndex],
9592
};
96-
};
93+
}
9794

9895
const handleOut = () => {
9996
mousePosition.current = {
@@ -118,8 +115,8 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
118115
dispatch({ type: 'exitChart' });
119116
return;
120117
}
121-
const newStateX = getUpdate(xAxis[usedXAxis], svgPoint.x);
122-
const newStateY = getUpdate(yAxis[usedYAxis], svgPoint.y);
118+
const newStateX = getNewAxisState(xAxis[usedXAxis], svgPoint.x);
119+
const newStateY = getNewAxisState(yAxis[usedYAxis], svgPoint.y);
123120

124121
dispatch({ type: 'updateAxis', data: { x: newStateX, y: newStateY } });
125122
};

packages/x-charts/src/hooks/useScale.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { AxisScaleConfig, D3Scale, ScaleName } from '../models/axis';
1010
*/
1111
export function getValueToPositionMapper(scale: D3Scale) {
1212
if (isBandScale(scale)) {
13-
return (value: any) => scale(value)! + scale.bandwidth() / 2;
13+
return (value: any) => (scale(value) ?? 0) + scale.bandwidth() / 2;
1414
}
1515
return (value: any) => scale(value) as number;
1616
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Transform mouse event position to coordinates inside the SVG.
3+
* @param svg The SVG element
4+
* @param event The mouseEvent to transform
5+
*/
6+
export function getSVGPoint(svg: SVGSVGElement, event: Pick<MouseEvent, 'clientX' | 'clientY'>) {
7+
const pt = svg.createSVGPoint();
8+
pt.x = event.clientX;
9+
pt.y = event.clientY;
10+
return pt.matrixTransform(svg.getScreenCTM()!.inverse());
11+
}

packages/x-charts/src/internals/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from '../ResponsiveChartContainer/ResizableContainer';
77
// hooks
88
export { useReducedMotion } from '../hooks/useReducedMotion';
99
export { useSeries } from '../hooks/useSeries';
10+
export { useDrawingArea } from '../hooks/useDrawingArea';
1011
export { useChartContainerHooks } from '../ChartContainer/useChartContainerHooks';
1112
export { useScatterChartProps } from '../ScatterChart/useScatterChartProps';
1213
export { useLineChartProps } from '../LineChart/useLineChartProps';
@@ -15,6 +16,7 @@ export { useBarChartProps } from '../BarChart/useBarChartProps';
1516
// utils
1617
export * from './defaultizeValueFormatter';
1718
export * from './configInit';
19+
export * from './getSVGPoint';
1820

1921
// contexts
2022

packages/x-charts/src/internals/utils.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@ export function getSymbol(shape: SymbolsTypes): number {
99
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
1010
export type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
1111

12-
/**
13-
* Transform mouse event position to coordinates inside the SVG.
14-
* @param svg The SVG element
15-
* @param event The mouseEvent to transform
16-
*/
17-
export function getSVGPoint(svg: SVGSVGElement, event: Pick<MouseEvent, 'clientX' | 'clientY'>) {
18-
const pt = svg.createSVGPoint();
19-
pt.x = event.clientX;
20-
pt.y = event.clientY;
21-
return pt.matrixTransform(svg.getScreenCTM()!.inverse());
22-
}
23-
2412
/**
2513
* Helper that converts values and percentages into values.
2614
* @param value The value provided by the developer. Can either be a number or a string with '%' or 'px'.

0 commit comments

Comments
 (0)