Skip to content

Conversation

@alexfauquette
Copy link
Member

This PR extract the keyboard behavior from the useKeboardNavigation to each series config

I replaced the custom FocusedItem by a new type derived from item identifier

@alexfauquette alexfauquette requested a review from a team as a code owner December 17, 2025 15:08
@mui-bot
Copy link

mui-bot commented Dec 17, 2025

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

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 🔺+298B(+0.09%) 🔺+121B(+0.12%)
@mui/x-charts-pro 🔺+298B(+0.07%) 🔺+94B(+0.07%)
@mui/x-charts-premium 🔺+298B(+0.07%) 🔺+92B(+0.07%)
@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 8ba94cb

@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 18, 2025
Comment on lines 11 to 40
const getNextFocusedItem: GetNextFocusedItem<'bar'> = (currentItem, event, state) => {
switch (event.key) {
case 'ArrowRight':
return getNextIndexFocusedItem<'bar', 'bar' | 'line' | 'scatter'>(
currentItem,
outSeriesTypes,
state,
);
case 'ArrowLeft':
return getPreviousIndexFocusedItem<'bar', 'bar' | 'line' | 'scatter'>(
currentItem,
outSeriesTypes,
state,
);
case 'ArrowDown':
return getPreviousSeriesFocusedItem<'bar', 'bar' | 'line' | 'scatter'>(
currentItem,
outSeriesTypes,
state,
);
case 'ArrowUp':
return getNextSeriesFocusedItem<'bar', 'bar' | 'line' | 'scatter'>(
currentItem,
outSeriesTypes,
state,
);
default:
return null;
}
};
Copy link
Member

Choose a reason for hiding this comment

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

if the api is the same for every item we could move the abstraction up so we don't need to split it here. We just care about the returned function

const getNextFocusedItem: GetNextFocusedItem<'bar'> = (event) => {
  switch (event.key) {
    case 'ArrowRight':
      return getNextIndexFocusedItem
    case 'ArrowLeft':
      return getPreviousIndexFocusedItem
    case 'ArrowDown':
      return getPreviousSeriesFocusedItem
    case 'ArrowUp':
      return getNextSeriesFocusedItem
    default:
      return null;
  }
};

then outSeriesTypes is just a const, can be part of the series config as well.

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 19, 2025

CodSpeed Performance Report

Merging #20693 will not alter performance

Comparing alexfauquette:move-keyboard-to-series-config (8ba94cb) with master (dcbbc64)1

Summary

✅ 14 untouched

Footnotes

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

@alexfauquette alexfauquette force-pushed the move-keyboard-to-series-config branch from 711a5d7 to aaee660 Compare December 19, 2025 16:56
@alexfauquette alexfauquette force-pushed the move-keyboard-to-series-config branch from aaee660 to b78384f Compare December 19, 2025 17:02
export function getNextSeriesWithData<
OutSeriesType extends Exclude<ChartSeriesType, 'sankey'> = Exclude<ChartSeriesType, 'sankey'>,
>(
series: ProcessedSeries<ChartSeriesType>,
Copy link
Member

Choose a reason for hiding this comment

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

Nit: can be done in a follow-up.

This type is broader than it needs to be. We only need the type to be { series: Array<{ data: unknown[] }>; seriesOrder: SeriesId[] }.

Should remove boilerplate from the tests (e.g., no need to specify the stackedData or minBarSize.

Copy link
Member Author

@alexfauquette alexfauquette Dec 22, 2025

Choose a reason for hiding this comment

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

That would require some extra processing. If I'm transforming the series look up into an array, I could go with a complitely different solution

  1. create the array
  2. find the index of the current series
  3. pick the one before or after

Looks simpler. I will do that in a follow up

Copy link
Member

Choose a reason for hiding this comment

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

That would require some extra processing

You're right, I wrote the wrong type. I meant something like this:

export type SeriesProcessorResult<TSeriesType extends ChartSeriesType> = {
  series: Record<SeriesId, { data: unknown[] }>;
  seriesOrder: SeriesId[];
};


type ProcessedSeries<TSeriesTypes extends ChartSeriesType = ChartSeriesType> = {
  [type in TSeriesTypes]?: SeriesProcessorResult<type>;
};

The only goal would be to avoid having to create boilerplate in tests. We only care about the length of the data of a series, so no need to set minBarSize, etc.

Copy link
Member

@JCQuintas JCQuintas left a comment

Choose a reason for hiding this comment

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

It seems there is an issue with the bar/line charts, possibly others as well, but it is harder to verify, where pressing the down arrow doesn't do the opposite of the up arrow.

Expected
Press up arrow: Select next series
Press down arrow: Select prev series

Actual
Press down arrow: Select prev ITEM

seriesIndex < seriesOfType.seriesOrder.length;
seriesIndex += 1
) {
if (seriesOfType.series[seriesOfType.seriesOrder[seriesIndex]].data.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Is a series where all data points are null considered an empty series?

return {
type,
seriesId,
dataIndex: Math.min(dataLength - 1, currentItem?.dataIndex ?? 0),
Copy link
Member

Choose a reason for hiding this comment

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

Should it be possible to focus items whose value is 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.

null values are not considered yet. For now it renders a focus object at 0

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 created #20746 to discuss this aspect

@alexfauquette
Copy link
Member Author

Press down arrow: Select prev ITEM

Yes a wrong copy past fixed by bernardo's review 👍

> = (
currentItem: (TSeriesType extends any ? FocusedItemIdentifier<TSeriesType> : never) | null,
state: ChartState<[UseChartKeyboardNavigationSignature], []>,
) => FocusedItemIdentifier<OutputSeriesType> | null;
Copy link
Member

Choose a reason for hiding this comment

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

ID is short for identifier. Should we call it FocusedItemId instead? It's bit shorter

Copy link
Member

Choose a reason for hiding this comment

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

We (charts) use identifier in general for object identifiers, cause ID could be misleading

Copy link
Member

Choose a reason for hiding this comment

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

We (charts) use identifier in general for object identifiers, cause ID could be misleading

) {
// @ts-ignore sankey is not in MIT version
if (type === 'sankey') {
return false;
Copy link
Member

Choose a reason for hiding this comment

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

Why do we want this to return false? Is it because we aren't implementing it for Sankey at the moment?

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 sankey is for now the only series type without a data property.

Technically I could also throw an error sinc this shoudl never occure (you're not supposed to mix a bar chart with a sankey)

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good 👍

Co-authored-by: Bernardo Belchior <[email protected]>
Signed-off-by: Alexandre Fauquette <[email protected]>
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