Skip to content

Commit 943e431

Browse files
[charts] Make axis highlight reflect the keyboard interaction (#19631)
Signed-off-by: Alexandre Fauquette <[email protected]> Co-authored-by: Bernardo Belchior <[email protected]>
1 parent 141e964 commit 943e431

File tree

7 files changed

+132
-11
lines changed

7 files changed

+132
-11
lines changed

packages/x-charts/src/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianHighlight.selectors.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import {
1010
import { ChartState } from '../../models/chart';
1111
import { UseChartCartesianAxisSignature } from './useChartCartesianAxis.types';
1212
import { ComputeResult } from './computeAxisValue';
13+
import {
14+
selectorChartsKeyboardXAxisIndex,
15+
selectorChartsKeyboardYAxisIndex,
16+
} from '../useChartKeyboardNavigation/useChartKeyboardNavigation.selectors';
17+
import { selectorChartsLastInteraction } from '../useChartInteraction/useChartInteraction.selectors';
18+
import { InteractionUpdateSource } from '../useChartInteraction/useChartInteraction.types';
1319

1420
const selectorChartControlledCartesianAxisHighlight = (
1521
state: ChartState<[], [UseChartCartesianAxisSignature]>,
@@ -48,19 +54,48 @@ const selectAxisHighlightWithValue = (
4854
computedIndex: number | null,
4955
computedValue: number | Date | null,
5056
axis: ComputeResult<ChartsAxisProps>,
51-
axisItems: AxisItemIdentifier[] | undefined,
57+
controlledAxisItems: AxisItemIdentifier[] | undefined,
58+
keyboardAxisItem: AxisItemIdentifier | undefined,
59+
lastInteractionUpdate: InteractionUpdateSource | undefined,
5260
) => {
53-
if (axisItems !== undefined) {
54-
return axisItems
61+
if (controlledAxisItems !== undefined) {
62+
return controlledAxisItems
5563
.map((item) => ({
5664
...item,
5765
value: axis.axis[item.axisId]?.data?.[item.dataIndex],
5866
}))
5967
.filter(({ value }) => value !== undefined);
6068
}
61-
return computedValue === null
62-
? []
63-
: [{ axisId: axis.axisIds[0], dataIndex: computedIndex, value: computedValue }];
69+
70+
const pointerHighlight = computedValue !== null && {
71+
axisId: axis.axisIds[0],
72+
dataIndex: computedIndex,
73+
value: computedValue,
74+
};
75+
const keyboardValue =
76+
keyboardAxisItem && axis.axis[keyboardAxisItem.axisId]?.data?.[keyboardAxisItem.dataIndex];
77+
const keyboardHighlight = keyboardAxisItem &&
78+
keyboardValue != null && { ...keyboardAxisItem, value: keyboardValue };
79+
80+
if (lastInteractionUpdate === 'pointer') {
81+
if (pointerHighlight) {
82+
return [pointerHighlight];
83+
}
84+
if (keyboardHighlight) {
85+
return [keyboardHighlight];
86+
}
87+
}
88+
89+
if (lastInteractionUpdate === 'keyboard') {
90+
if (keyboardHighlight) {
91+
return [keyboardHighlight];
92+
}
93+
if (pointerHighlight) {
94+
return [pointerHighlight];
95+
}
96+
}
97+
98+
return [];
6499
};
65100

66101
export const selectorChartsHighlightXAxisValue = createSelector(
@@ -69,6 +104,8 @@ export const selectorChartsHighlightXAxisValue = createSelector(
69104
selectorChartsInteractionXAxisValue,
70105
selectorChartXAxis,
71106
selectorChartControlledCartesianAxisHighlight,
107+
selectorChartsKeyboardXAxisIndex,
108+
selectorChartsLastInteraction,
72109
],
73110
selectAxisHighlightWithValue,
74111
);
@@ -79,6 +116,8 @@ export const selectorChartsHighlightYAxisValue = createSelector(
79116
selectorChartsInteractionYAxisValue,
80117
selectorChartYAxis,
81118
selectorChartControlledCartesianAxisHighlight,
119+
selectorChartsKeyboardYAxisIndex,
120+
selectorChartsLastInteraction,
82121
],
83122
selectAxisHighlightWithValue,
84123
);

packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.selectors.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ export const selectorChartsInteractionItemIsDefined = createSelector(
3333
[selectorChartsInteractionItem],
3434
(item) => item !== null,
3535
);
36+
37+
export const selectorChartsLastInteraction = createSelector(
38+
[selectInteraction],
39+
(interaction) => interaction?.lastUpdate,
40+
);

packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const useChartInteraction: ChartPlugin<UseChartInteractionSignature> = ({
1313
store.update((prev) => {
1414
return {
1515
...prev,
16-
interaction: { pointer: null, item: null },
16+
interaction: { ...prev.interaction, pointer: null, item: null },
1717
};
1818
});
1919
});
@@ -85,6 +85,7 @@ export const useChartInteraction: ChartPlugin<UseChartInteractionSignature> = ({
8585
interaction: {
8686
...prev.interaction,
8787
pointer: coordinate,
88+
lastUpdate: coordinate !== null ? 'pointer' : prev.interaction.lastUpdate,
8889
},
8990
}));
9091
});
@@ -100,7 +101,7 @@ export const useChartInteraction: ChartPlugin<UseChartInteractionSignature> = ({
100101
};
101102

102103
useChartInteraction.getInitialState = () => ({
103-
interaction: { item: null, pointer: null },
104+
interaction: { item: null, pointer: null, lastUpdate: 'pointer' },
104105
});
105106

106107
useChartInteraction.params = {};

packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77

88
export type Coordinate = { x: number; y: number };
99

10+
export type InteractionUpdateSource = 'pointer' | 'keyboard';
11+
1012
export interface UseChartInteractionInstance {
1113
/**
1214
* Remove all interaction.
@@ -39,6 +41,11 @@ export interface UseChartInteractionState {
3941
* The x/y SVG coordinate of the "main" pointer
4042
*/
4143
pointer: Coordinate | null;
44+
/**
45+
* The last interaction highlight update.
46+
* Used to decide if highlight should be based on pointer position or keyboard navigation.
47+
*/
48+
lastUpdate: InteractionUpdateSource;
4249
};
4350
}
4451

packages/x-charts/src/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { ChartOptionalRootSelector, createSelector } from '../../utils/selectors';
22
import { UseChartKeyboardNavigationSignature } from './useChartKeyboardNavigation.types';
3+
import { ProcessedSeries, selectorChartSeriesProcessed } from '../../corePlugins/useChartSeries';
4+
import {
5+
selectorChartXAxis,
6+
selectorChartYAxis,
7+
} from '../useChartCartesianAxis/useChartCartesianAxisRendering.selectors';
8+
import { ComputeResult } from '../useChartCartesianAxis/computeAxisValue';
9+
import { ChartSeriesType } from '../../../../models/seriesType/config';
10+
import { SeriesId } from '../../../../models/seriesType/common';
11+
import { AxisId, AxisItemIdentifier, ChartsAxisProps } from '../../../../models/axis';
312

413
const selectKeyboardNavigation: ChartOptionalRootSelector<UseChartKeyboardNavigationSignature> = (
514
state,
@@ -29,3 +38,59 @@ export const selectorChartsIsKeyboardNavigationEnabled = createSelector(
2938
[selectKeyboardNavigation],
3039
(keyboardNavigationState) => !!keyboardNavigationState?.enableKeyboardNavigation,
3140
);
41+
42+
/**
43+
* Selectors to override highlight behavior.
44+
*/
45+
46+
const createSelectAxisHighlight =
47+
(direction: 'x' | 'y') =>
48+
<T extends ChartSeriesType>(
49+
type: T | undefined,
50+
seriesId: SeriesId | undefined,
51+
dataIndex: number | undefined,
52+
axis: ComputeResult<ChartsAxisProps>,
53+
series: ProcessedSeries<T>,
54+
): AxisItemIdentifier | undefined => {
55+
if (type === undefined || seriesId === undefined || dataIndex === undefined) {
56+
return undefined;
57+
}
58+
59+
const seriesConfig = series[type]?.series[seriesId];
60+
if (!seriesConfig) {
61+
return undefined;
62+
}
63+
64+
let axisId: AxisId | false | undefined =
65+
direction === 'x'
66+
? 'xAxisId' in seriesConfig && seriesConfig.xAxisId
67+
: 'yAxisId' in seriesConfig && seriesConfig.yAxisId;
68+
69+
if (axisId === undefined || axisId === false) {
70+
axisId = axis.axisIds[0];
71+
}
72+
73+
return { axisId, dataIndex };
74+
};
75+
76+
export const selectorChartsKeyboardXAxisIndex = createSelector(
77+
[
78+
selectorChartsFocusedSeriesType,
79+
selectorChartsFocusedSeriesId,
80+
selectorChartsFocusedDataIndex,
81+
selectorChartXAxis,
82+
selectorChartSeriesProcessed,
83+
],
84+
createSelectAxisHighlight('x'),
85+
);
86+
87+
export const selectorChartsKeyboardYAxisIndex = createSelector(
88+
[
89+
selectorChartsFocusedSeriesType,
90+
selectorChartsFocusedSeriesId,
91+
selectorChartsFocusedDataIndex,
92+
selectorChartYAxis,
93+
selectorChartSeriesProcessed,
94+
],
95+
createSelectAxisHighlight('y'),
96+
);

packages/x-charts/src/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export const useChartKeyboardNavigation: ChartPlugin<UseChartKeyboardNavigationS
123123

124124
React.useEffect(() => {
125125
const element = svgRef.current;
126+
126127
if (!element || !params.enableKeyboardNavigation) {
127128
return undefined;
128129
}
@@ -154,6 +155,9 @@ export const useChartKeyboardNavigation: ChartPlugin<UseChartKeyboardNavigationS
154155
event.preventDefault();
155156
return {
156157
...prevState,
158+
...(prevState.interaction && {
159+
interaction: { ...prevState.interaction, lastUpdate: 'keyboard' },
160+
}),
157161
keyboardNavigation: {
158162
...prevState.keyboardNavigation,
159163
item: newFocusedItem,
@@ -190,9 +194,7 @@ export const useChartKeyboardNavigation: ChartPlugin<UseChartKeyboardNavigationS
190194
[store, params.enableKeyboardNavigation],
191195
);
192196

193-
return {
194-
instance: {},
195-
};
197+
return {};
196198
};
197199

198200
useChartKeyboardNavigation.getInitialState = (params) => ({

packages/x-charts/src/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChartPluginSignature } from '../../models';
22
import { ChartSeriesType } from '../../../../models/seriesType/config';
33
import { SeriesId } from '../../../../models/seriesType/common';
4+
import { UseChartInteractionSignature } from '../useChartInteraction';
45

56
export interface UseChartKeyboardNavigationInstance {}
67

@@ -35,4 +36,5 @@ export type UseChartKeyboardNavigationSignature = ChartPluginSignature<{
3536
defaultizedParams: UseChartKeyboardNavigationParameters;
3637
instance: UseChartKeyboardNavigationInstance;
3738
state: UseChartKeyboardNavigationState;
39+
optionalDependencies: [UseChartInteractionSignature];
3840
}>;

0 commit comments

Comments
 (0)