-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[charts] Extract keyboard focus navigation to the series config #20693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[charts] Extract keyboard focus navigation to the series config #20693
Conversation
|
Deploy preview: https://deploy-preview-20693--material-ui-x.netlify.app/ Bundle size report
|
packages/x-charts/src/internals/plugins/models/seriesConfig/getNextFocusedItem.types.ts
Outdated
Show resolved
Hide resolved
| 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; | ||
| } | ||
| }; |
There was a problem hiding this comment.
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.
packages/x-charts/src/internals/plugins/models/seriesConfig/seriesConfig.types.ts
Outdated
Show resolved
Hide resolved
CodSpeed Performance ReportMerging #20693 will not alter performanceComparing Summary
Footnotes |
711a5d7 to
aaee660
Compare
aaee660 to
b78384f
Compare
packages/x-charts/src/LineChart/seriesConfig/keyboardFocusHandler.ts
Outdated
Show resolved
Hide resolved
...rc/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.ts
Outdated
Show resolved
Hide resolved
...rc/internals/plugins/featurePlugins/useChartKeyboardNavigation/keyboardFocusHandler.types.ts
Outdated
Show resolved
Hide resolved
...ternals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getPreviousSeriesWithData.ts
Outdated
Show resolved
Hide resolved
| export function getNextSeriesWithData< | ||
| OutSeriesType extends Exclude<ChartSeriesType, 'sankey'> = Exclude<ChartSeriesType, 'sankey'>, | ||
| >( | ||
| series: ProcessedSeries<ChartSeriesType>, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
- create the array
- find the index of the current series
- pick the one before or after
Looks simpler. I will do that in a follow up
There was a problem hiding this comment.
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.
JCQuintas
left a comment
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
Yes a wrong copy past fixed by bernardo's review 👍 |
| > = ( | ||
| currentItem: (TSeriesType extends any ? FocusedItemIdentifier<TSeriesType> : never) | null, | ||
| state: ChartState<[UseChartKeyboardNavigationSignature], []>, | ||
| ) => FocusedItemIdentifier<OutputSeriesType> | null; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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]>
This PR extract the keyboard behavior from the useKeboardNavigation to each series config
I replaced the custom
FocusedItemby a new type derived from item identifier