Skip to content

Conversation

@alexfauquette
Copy link
Member

This is a preliminary PR before the one to control tooltip item

The useChartInteraction handles at the same time the follwoing state

  • item : defines the item tooltip
  • pointer: {x, y}: defines the current coordinate of the pointer
  • lastUpdate: the interaction that triggered the last update (keyboard or pointer)

In terms of usage:

  • item : only for the item tooltip
  • pointer: {x, y}: for both the axis tooltip, and the axis highlight
  • lastUpdate: for both tooltip and highlight

The idea is to keep in interaction only the state that is shared between multiple concept (highlight and tooltip) and in the tooltip plugin put what is only related to the tooltip.


The PR is splitted by commits to simplify the review

@alexfauquette alexfauquette requested review from a team and prakhargupta1 as code owners December 8, 2025 10:03
@alexfauquette alexfauquette added type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. scope: charts Changes related to the charts. labels Dec 8, 2025
@mui-bot
Copy link

mui-bot commented Dec 8, 2025

Deploy preview: https://deploy-preview-20591--material-ui-x.netlify.app/

Updated pages:

Bundle size report

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 🔺+258B(+0.08%) ▼-5B(0.00%)
@mui/x-charts-pro 🔺+276B(+0.06%) 🔺+28B(+0.02%)
@mui/x-charts-premium 🔺+254B(+0.06%) 🔺+31B(+0.02%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against f626002

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 8, 2025

CodSpeed Performance Report

Merging #20591 will not alter performance

Comparing alexfauquette:tooltip-item-control (f626002) with master (f4a21f7)1

Summary

✅ 13 untouched

Footnotes

  1. No successful run was found on master (249684a) during the generation of this report, so f4a21f7 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Comment on lines -13 to -17
export const selectorChartsInteractionItem = createSelector(
selectInteraction,
(interaction) => interaction?.item ?? null,
);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by selectorChartsTooltipPointerItem

Comment on lines -33 to -36
export const selectorChartsInteractionItemIsDefined = createSelector(
selectorChartsInteractionItem,
(item) => item !== null,
);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by selectorChartsTooltipPointerItemIsDefined

store.set('interaction', { ...store.state.interaction, item: null });
store.update({
interaction: { ...store.state.interaction, pointer: null },
tooltip: { ...store.state.tooltip, item: null },
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the tooltip update here avoid to modify too many lines. Migth need to remove it when introducing the control

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why though? If the goal is to split them, shouldn't we isolate them more?

Or maybe do it in a new PR if it needs changing a lot of files 😆

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I realized that while reviewing my own PR :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it by modifying the "closest point" plugin because it's able to set the tooltip item. But I did not modify the cartesian/polar axis plugins, because they do not set values to the item tooltip.

store.set('interaction', { ...store.state.interaction, item: null });
store.update({
interaction: { ...store.state.interaction, pointer: null },
tooltip: { ...store.state.tooltip, item: null },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why though? If the goal is to split them, shouldn't we isolate them more?

Or maybe do it in a new PR if it needs changing a lot of files 😆

Comment on lines 23 to 26
Object.keys(itemToRemove).some(
(key) =>
itemToRemove[key as keyof typeof itemToRemove] !== prevItem[key as keyof typeof prevItem],
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cant we use it like this?

Suggested change
Object.keys(itemToRemove).some(
(key) =>
itemToRemove[key as keyof typeof itemToRemove] !== prevItem[key as keyof typeof prevItem],
)
Object.keys(itemToRemove).some(
(key: keyof ChartItemIdentifier<ChartSeriesType>) =>
itemToRemove[key] !== prevItem[key],
)

Copy link
Member Author

@alexfauquette alexfauquette Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but this work

(Object.keys(itemToRemove) as Array<keyof ChartItemIdentifier<ChartSeriesType>>).some(
        (key) =>
          itemToRemove[key] !== prevItem[key],
      )


export interface UseChartTooltipInstance {
/**
* Setter for the item the user is interacting with.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update these docs? "Interaction" and "interacting" may not make sense anymore

* Setter for the item the user is interacting with.
* @param {ChartItemIdentifier} newItem The identifier of the item.
*/
setItemTooltip: (newItem: ChartItemIdentifier<ChartSeriesType>) => void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do this later, but wouldn't it make more sense to call this setTooltipItem?

*/
setItemTooltip: (newItem: ChartItemIdentifier<ChartSeriesType>) => void;
/**
* Remove item interaction if the current if the provided item is still the one interacting.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case where itemToRemove isn't provided is not documented

Comment on lines 23 to 25
(Object.keys(itemToRemove) as Array<keyof ChartItemIdentifier<ChartSeriesType>>).some(
(key) => itemToRemove[key] !== prevItem[key],
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use fastObjectShallowCompare here?

* @param {InteractionUpdateSource} interaction The source of the last interaction update (pointer or keyboard)
* @returns {void}
*/
setLastUpdate: (interaction: InteractionUpdateSource) => void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setLastUpdate: (interaction: InteractionUpdateSource) => void;
setLastUpdateSource: (interaction: InteractionUpdateSource) => void;

Comment on lines 167 to 169
instance.removeItemTooltip?.();
instance.clearHighlight?.();
instance.removeItemTooltip?.();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to call removeItemTooltip? Seems a bit weird

export type UseChartInteractionSignature = ChartPluginSignature<{
instance: UseChartInteractionInstance;
state: UseChartInteractionState;
optionalDependencies: [UseChartTooltipSignature];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this plugin depend on the tooltip plugin? I couldn't find any reference to the tooltip plugin from this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. It's because initially the cleanInteraction() was also resetting the tooltip item

@alexfauquette alexfauquette merged commit d72d0f4 into mui:master Dec 9, 2025
22 checks passed
A-s-h-o-k pushed a commit to A-s-h-o-k/mui-x that referenced this pull request Dec 14, 2025
mapache-salvaje pushed a commit to mapache-salvaje/mui-x that referenced this pull request Dec 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: charts Changes related to the charts. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants