Skip to content

Commit 79b08d9

Browse files
authored
fix(react-charting): callout not appearing on scatter chart (microsoft#35185)
1 parent 40df94f commit 79b08d9

File tree

6 files changed

+152
-166
lines changed

6 files changed

+152
-166
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix: callout not appearing on scatter chart",
4+
"packageName": "@fluentui/react-charting",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/charts/react-charting/src/components/AreaChart/AreaChart.base.tsx

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { select as d3Select } from 'd3-selection';
55
import { area as d3Area, stack as d3Stack, curveMonotoneX as d3CurveBasis, line as d3Line } from 'd3-shape';
66
import {
77
classNamesFunction,
8-
find,
98
getId,
109
getRTL,
1110
initializeComponentRef,
@@ -45,6 +44,7 @@ import {
4544
areArraysEqual,
4645
getCurveFactory,
4746
YAxisType,
47+
findCalloutPoints,
4848
} from '../../utilities/index';
4949
import { ILegend, ILegendContainer, Legends } from '../Legends/index';
5050
import { DirectionalHint } from '@fluentui/react/lib/Callout';
@@ -391,11 +391,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
391391
pointToHighlight instanceof Date
392392
? formatDateToLocaleString(pointToHighlight, this.props.culture, this.props.useUTC)
393393
: pointToHighlight;
394-
const modifiedXVal = pointToHighlight instanceof Date ? pointToHighlight.getTime() : pointToHighlight;
395-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
396-
const found: any = find(this._calloutPoints, (element: { x: string | number }) => {
397-
return element.x === modifiedXVal;
398-
});
394+
const found = findCalloutPoints(this._calloutPoints, pointToHighlight);
399395
const nearestCircleToHighlight =
400396
axisType === XAxisTypes.DateAxis ? (pointToHighlight as Date).getTime() : pointToHighlight;
401397
const pointToHighlightUpdated = this.state.nearestCircleToHighlight !== nearestCircleToHighlight;
@@ -1163,22 +1159,22 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
11631159
private _handleFocus = (lineIndex: number, pointIndex: number, circleId: string) => {
11641160
const { x, y, xAxisCalloutData } = this.props.data.lineChartData![lineIndex].data[pointIndex];
11651161
const formattedDate = x instanceof Date ? formatDateToLocaleString(x, this.props.culture, this.props.useUTC) : x;
1166-
const modifiedXVal = x instanceof Date ? x.getTime() : x;
1167-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1168-
const found: any = this._calloutPoints.find((e: { x: string | number }) => e.x === modifiedXVal);
1169-
// Show details in the callout for the focused point only
1170-
found.values = found.values.filter((e: { y: number }) => e.y === y);
1171-
const filteredValues = this._getFilteredLegendValues(found.values);
1162+
const found = findCalloutPoints(this._calloutPoints, x);
1163+
if (found) {
1164+
// Show details in the callout for the focused point only
1165+
found.values = found.values.filter((e: { y: number }) => e.y === y);
1166+
const filteredValues = this._getFilteredLegendValues(found.values);
11721167

1173-
this.setState({
1174-
refSelected: `#${circleId}`,
1175-
isCalloutVisible: true,
1176-
hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate,
1177-
YValueHover: filteredValues!,
1178-
stackCalloutProps: { ...found, values: filteredValues },
1179-
dataPointCalloutProps: { ...found, values: filteredValues },
1180-
activePoint: circleId,
1181-
});
1168+
this.setState({
1169+
refSelected: `#${circleId}`,
1170+
isCalloutVisible: true,
1171+
hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate,
1172+
YValueHover: filteredValues!,
1173+
stackCalloutProps: { ...found, values: filteredValues },
1174+
dataPointCalloutProps: { ...found, values: filteredValues },
1175+
activePoint: circleId,
1176+
});
1177+
}
11821178
};
11831179

11841180
// eslint-disable-next-line @typescript-eslint/no-explicit-any

packages/charts/react-charting/src/components/LineChart/LineChart.base.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ILineChartStyles,
3030
ILineChartGap,
3131
ILineChartDataPoint,
32+
IYValueHover,
3233
} from '../../index';
3334
import { DirectionalHint } from '@fluentui/react/lib/Callout';
3435
import { formatDateToLocaleString } from '@fluentui/chart-utilities';
@@ -57,6 +58,7 @@ import {
5758
getDomainPaddingForMarkers,
5859
isPlottable,
5960
getRangeForScatterMarkerSize,
61+
findCalloutPoints,
6062
} from '../../utilities/index';
6163
import { IChart, IImageExportOptions } from '../../types/index';
6264
import { toImage } from '../../utilities/image-export-utils';
@@ -178,8 +180,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
178180
};
179181

180182
private _points: LineChartDataWithIndex[];
181-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
182-
private _calloutPoints: any[];
183+
private _calloutPoints: Record<string, IYValueHover[]>;
183184
// eslint-disable-next-line @typescript-eslint/no-explicit-any
184185
private _xAxisScale: any = '';
185186
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -237,7 +238,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
237238
this._refArray = [];
238239
this._points = this._injectIndexPropertyInLineChartData(this.props.data.lineChartData);
239240
this._colorFillBars = [];
240-
this._calloutPoints = calloutData(this._points) || [];
241+
this._calloutPoints = calloutData(this._points) || {};
241242
this._circleId = getId('circle');
242243
this._lineId = getId('lineID');
243244
this._borderId = getId('borderID');
@@ -278,7 +279,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
278279
prevProps.data !== this.props.data
279280
) {
280281
this._points = this._injectIndexPropertyInLineChartData(this.props.data.lineChartData);
281-
this._calloutPoints = calloutData(this._points) || [];
282+
this._calloutPoints = calloutData(this._points) || {};
282283
}
283284
}
284285

@@ -1597,11 +1598,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
15971598
xPointToHighlight instanceof Date
15981599
? formatDateToLocaleString(xPointToHighlight, this.props.culture, this.props.useUTC)
15991600
: xPointToHighlight;
1600-
const modifiedXVal = xPointToHighlight instanceof Date ? xPointToHighlight.getTime() : xPointToHighlight;
1601-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1602-
const found: any = find(this._calloutPoints, (element: { x: string | number }) => {
1603-
return element.x === modifiedXVal;
1604-
});
1601+
const found = findCalloutPoints(this._calloutPoints, xPointToHighlight) as ICustomizedCalloutData | undefined;
16051602
const pointToHighlight: ILineChartDataPoint = lineChartData![linenumber].data[index!];
16061603
const pointToHighlightUpdated =
16071604
this.state.nearestCircleToHighlight === null ||
@@ -1657,8 +1654,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
16571654
) => {
16581655
this._uniqueCallOutID = circleId;
16591656
const formattedData = x instanceof Date ? formatDateToLocaleString(x, this.props.culture, this.props.useUTC) : x;
1660-
const xVal = x instanceof Date ? x.getTime() : x;
1661-
const found = find(this._calloutPoints, (element: { x: string | number }) => element.x === xVal);
1657+
const found = findCalloutPoints(this._calloutPoints, x) as ICustomizedCalloutData | undefined;
16621658
// if no points need to be called out then don't show vertical line and callout card
16631659

16641660
if (found) {
@@ -1689,7 +1685,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
16891685

16901686
private _handleHover = (
16911687
x: number | Date,
1692-
y: number | Date,
1688+
y: number,
16931689
lineHeight: number,
16941690
xAxisCalloutData: string | undefined,
16951691
circleId: string,
@@ -1701,17 +1697,15 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
17011697
) => {
17021698
mouseEvent.persist();
17031699
const formattedData = x instanceof Date ? formatDateToLocaleString(x, this.props.culture, this.props.useUTC) : x;
1704-
const xVal = x instanceof Date ? x.getTime() : x;
1705-
const yVal = y instanceof Date ? y.getTime() : y;
17061700
const _this = this;
1707-
const found = find(this._calloutPoints, (element: { x: string | number }) => element.x === xVal);
1701+
const found = findCalloutPoints(this._calloutPoints, x) as ICustomizedCalloutData | undefined;
17081702
let hoverDp: ICustomizedCalloutData | undefined = undefined;
17091703

17101704
if (this.props.isCalloutForStack === false && found?.values) {
1711-
const dp = find(found.values, (val: ICustomizedCalloutDataPoint) => val?.y === yVal);
1705+
const dp = find(found.values, (val: ICustomizedCalloutDataPoint) => val?.y === y);
17121706
if (dp) {
17131707
hoverDp = {
1714-
x: xVal,
1708+
x,
17151709
values: [dp],
17161710
};
17171711
}
@@ -1731,7 +1725,7 @@ export class LineChartBase extends React.Component<ILineChartProps, ILineChartSt
17311725
refSelected: `#${circleId}`,
17321726
hoverXValue: xAxisCalloutData ? xAxisCalloutData : '' + formattedData,
17331727
YValueHover: found.values,
1734-
YValue: yVal,
1728+
YValue: y,
17351729
legendVal: legendVal!,
17361730
lineColor,
17371731
stackCalloutProps: found!,

packages/charts/react-charting/src/components/ScatterChart/ScatterChart.base.tsx

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
getRangeForScatterMarkerSize,
1919
domainRangeOfDateForAreaLineScatterVerticalBarCharts,
2020
domainRangeOfNumericForAreaLineScatterCharts,
21+
findCalloutPoints,
2122
} from '../../utilities/index';
2223
import {
2324
IAccessibilityProps,
@@ -26,6 +27,7 @@ import {
2627
IMargins,
2728
IRefArrayData,
2829
IChart,
30+
IYValueHover,
2931
} from '../../index';
3032
import {
3133
calloutData,
@@ -37,7 +39,7 @@ import {
3739
getColorFromToken,
3840
sortAxisCategories,
3941
} from '../../utilities/index';
40-
import { classNamesFunction, DirectionalHint, find, getId, getRTL } from '@fluentui/react';
42+
import { classNamesFunction, DirectionalHint, getId, getRTL } from '@fluentui/react';
4143
import { IImageExportOptions, IScatterChartDataPoint, IScatterChartPoints } from '../../types/index';
4244
import { ILineChartPoints } from '../../types/IDataPoint';
4345
import { toImage as convertToImage } from '../../utilities/image-export-utils';
@@ -67,8 +69,7 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
6769
const _points = React.useRef<ScatterChartDataWithIndex[]>(
6870
_injectIndexPropertyInScatterChartData(props.data.scatterChartData),
6971
);
70-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
71-
let _calloutPoints = React.useMemo(() => calloutData(_points.current as any) || [], [_points]);
72+
let _calloutPoints = React.useMemo(() => calloutData(_points.current) || {}, [_points]);
7273
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7374
const _xAxisScale: any = React.useRef<any>('');
7475
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -86,7 +87,7 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
8687

8788
const [hoverXValue, setHoverXValue] = React.useState<string | number>('');
8889
const [activeLegend, setActiveLegend] = React.useState<string>('');
89-
const [YValueHover, setYValueHover] = React.useState<[]>([]);
90+
const [YValueHover, setYValueHover] = React.useState<IYValueHover[]>([]);
9091
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9192
const [selectedLegendPoints, setSelectedLegendPoints] = React.useState<any[]>([]);
9293
const [isSelectedLegend, setIsSelectedLegend] = React.useState<boolean>(false);
@@ -144,8 +145,6 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
144145
: YAxisType.NumericAxis;
145146

146147
const pointsRef = React.useRef<ScatterChartDataWithIndex[] | []>([]);
147-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
148-
const calloutPointsRef = React.useRef<any[]>([]);
149148
React.useEffect(() => {
150149
/** note that height and width are not used to resize or set as dimesions of the chart,
151150
* fitParentContainer is responisble for setting the height and width or resizing of the svg/chart
@@ -156,7 +155,6 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
156155
props.data !== _points.current
157156
) {
158157
pointsRef.current = _injectIndexPropertyInScatterChartData(props.data.scatterChartData);
159-
calloutPointsRef.current = calloutData(pointsRef.current);
160158
}
161159
}, [props.height, props.width, props.data, _points]);
162160

@@ -352,10 +350,7 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
352350
) => {
353351
_uniqueCallOutID.current = circleId;
354352
const formattedData = x instanceof Date ? formatDateToLocaleString(x, props.culture, props.useUTC) : x;
355-
const xVal = x instanceof Date ? x.getTime() : x;
356-
const found = find(_calloutPoints, (element: { x: string | number }) => element.x === xVal) as
357-
| { x: string | number; values: [] }
358-
| undefined;
353+
const found = findCalloutPoints(_calloutPoints, x);
359354
// if no points need to be called out then don't show vertical line and callout card
360355
if (found) {
361356
d3Select(`#${_verticalLine}`)
@@ -380,7 +375,7 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
380375
const _handleHover = React.useCallback(
381376
(
382377
x: number | Date | string,
383-
y: number | Date,
378+
y: number,
384379
lineHeight: number,
385380
xAxisCalloutData: string | undefined,
386381
circleId: string,
@@ -389,10 +384,7 @@ export const ScatterChartBase: React.FunctionComponent<IScatterChartProps> = Rea
389384
) => {
390385
mouseEvent?.persist();
391386
const formattedData = x instanceof Date ? formatDateToLocaleString(x, props.culture, props.useUTC) : x;
392-
const xVal = x instanceof Date ? x.getTime() : x;
393-
const found = find(_calloutPoints, (element: { x: string | number }) => element.x === xVal) as
394-
| { x: string | number; values: [] }
395-
| undefined;
387+
const found = findCalloutPoints(_calloutPoints, x) as ICustomizedCalloutData | undefined;
396388
// if no points need to be called out then don't show vertical line and callout card
397389
if (found) {
398390
d3Select(`#${_verticalLine}`)

0 commit comments

Comments
 (0)