Skip to content
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

[DataGrid] Store the outlined cell in the state #7111

Merged
merged 11 commits into from
Jan 6, 2023

Conversation

m4theushw
Copy link
Member

@m4theushw m4theushw commented Dec 7, 2022

Part of #6567 (comment)

Fixes #3844

Here I'm moving to the state the information of which cell has the outline. Previously, the outline style was added by CSS using :focus-within, but for cell selection a cell might focused but not have the outline. During normal usage, e.g. keyboard navigation, the outline is synced with the focus inside apiRef.current.setCellFocus. This means that if the focus is not correctly updated, the cell won't have the outline. A way to review this PR is to battle test the focus logic ensuring that the focus is always updated.

To implement the correct behavior for cell selection I'll open another PR allowing apiRef.current.setCellFocus to skip the outline update. Then we can add a apiRef.current.setOutlinedCell to update only the outline state.

Release

Breaking changes

  • To update the outline style of a focused cell, use the .MuiDataGrid-cell--outlined CSS class instead of the :focus-within selector.

    -'.MuiDataGrid-cell:focus-within': {
    +'.MuiDataGrid-cell--outlined': {

@m4theushw m4theushw added the component: data grid This is the name of the generic UI component, not the React module! label Dec 7, 2022
@mui-bot
Copy link

mui-bot commented Dec 7, 2022

Messages
📖 Netlify deploy preview: https://deploy-preview-7111--material-ui-x.netlify.app/

These are the results for the performance tests:

Test case Unit Min Max Median Mean σ
Filter 100k rows ms 663.9 1,086.2 663.9 819.42 186.867
Sort 100k rows ms 617.4 1,176.4 1,176.4 958.38 216.739
Select 100k rows ms 227.5 329.3 290.3 279.32 35.852
Deselect 100k rows ms 151.2 326.4 201.3 214.86 60.098

Generated by 🚫 dangerJS against c7ea049

@@ -224,7 +258,7 @@ export const useGridFocus = (
[apiRef],
);

const focussedColumnGroup = unstable_gridFocusColumnGroupHeaderSelector(apiRef);
const focusedColumnGroup = unstable_gridFocusColumnGroupHeaderSelector(apiRef);
Copy link
Member Author

Choose a reason for hiding this comment

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

Just a typo.

Comment on lines +232 to +249
const handleCellBlur = useEventCallback(() => {
if (lastKeydownEvent.current?.key !== 'Tab') {
return;
}
apiRef.current.setState((state) => ({
...state,
// The tabIndex is kept to allow the focus to return to this cell if Shift+Tab is pressed
focus: { cell: null, columnHeader: null, columnGroupHeader: null },
outline: { cell: null },
}));
apiRef.current.forceUpdate();
});

const handleCellFocus = useEventCallback((params: GridCellParams) => {
if (lastKeydownEvent.current?.key === 'Tab') {
apiRef.current.setCellFocus(params.id, params.field);
}
});
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the fix for #3844


if (
process.env.NODE_ENV === 'test' &&
rootProps.experimentalFeatures?.warnIfFocusStateIsNotSynced
Copy link
Member Author

Choose a reason for hiding this comment

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

This flag is useless now because when a cell is focused via keyboard, onFocus is called before we update the focus state. I don't consider a breaking change because it's already experimental and it didn't have a good reception: #3850

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Dec 13, 2022
@github-actions
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@DanailH DanailH changed the title [DataGrid] Store in the state the cell outlined [DataGrid] Store the outlined cell in the state Dec 15, 2022
Copy link
Member

@DanailH DanailH left a comment

Choose a reason for hiding this comment

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

Nice work! I just have a couple of notes/questions but other than those it's good from my side

apiRef.current.setState((state) => ({
...state,
// The tabIndex is kept to allow the focus to return to this cell if Shift+Tab is pressed
focus: { cell: null, columnHeader: null, columnGroupHeader: null },
Copy link
Member

Choose a reason for hiding this comment

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

It's more of a general question but do we also have a focus on a row? I'm asking because of the row selection and things like row grouping.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, because rows don't have the outline.

packages/grid/x-data-grid/src/models/api/gridFocusApi.ts Outdated Show resolved Hide resolved
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 2, 2023
@@ -124,7 +124,7 @@ export const GridRootStyles = styled('div', {
padding: '0 10px',
boxSizing: 'border-box',
},
[`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses.cell}:focus-within`]: {
[`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses['cell--outlined']}`]: {
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 ditch :focus-within in the column header as well?

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 don't think we need to do this change now. Creating a .MuiDataGrid-columnHeader--outlined will create complexity that's not necessary for now. If some day we implement a range selection for column headers, then we'll need to do this.

@@ -6,6 +6,13 @@
"params": "GridAggregationModel",
"event": "MuiEvent<{}>"
},
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "cellBlur",
Copy link
Member

Choose a reason for hiding this comment

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

Do we still need cellFocusIn/cellFocusOut events?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, for editing. cellBlur is tied to the blur DOM event, meaning that it's published every time a cell loses focus, including when the focus is lost to a portaled element inside the same cell. In the other hand, cellFocusOut is only published when focus state is updated. If a cell loses focus to an portaled element inside it, cellFocusOut is not published but cellBlur is.

- To update the outline style of a focused cell, use the `.MuiDataGrid-cell--outlined` class instead of the `:focus-within` selector.
```diff
-'.MuiDataGrid-cell:focus-within': {
+'.MuiDataGrid-cell--outlined': {
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
+'.MuiDataGrid-cell--outlined': {
+'.MuiDataGrid-cell--outlined': {
- `.${gridClasses.cell}:focus-within`
+ `.${gridClasses['cell--outlined']}`

Maybe include an example with gridClasses as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added!

@cherniavskii
Copy link
Member

To implement the correct behavior for cell selection I'll open another PR allowing apiRef.current.setCellFocus to skip the outline update. Then we can add a apiRef.current.setOutlinedCell to update only the outline state.

I'm not sure we have to decouple focus/outline states to implement the correct behavior for cell selection.
Did you consider storing the range end cell in the state instead? This should be enough information how to adjust the range after initial selection

@m4theushw
Copy link
Member Author

Did you consider storing the range end cell in the state instead? This should be enough information how to adjust the range after initial selection

@cherniavskii I'm not sure I understood you. The problem is not to know which cell was the last selected, but to put the outline in another cell. By default, the outlined cell is the last one clicked. In a range selection created by clicking two cells, the outlined cell is the last one, however, I need to keep the outline in the first clicked cell.

@cherniavskii
Copy link
Member

In a range selection created by clicking two cells, the outlined cell is the last one, however, I need to keep the outline in the first clicked cell.

Sorry, I thought you were solving a different issue.
Could you provide more details on the issue you're solving? I don't understand why the outline has to stay in the first-clicked cell.

@m4theushw
Copy link
Member Author

Could you provide more details on the issue you're solving? I don't understand why the outline has to stay in the first-clicked cell.

@cherniavskii The outline has to stay in the first-clicked cell only in cell selection. It's how most spreadsheet apps work: #6567 (comment) For everything else nothing has changed, the outlined cell is always the last clicked cell.

@m4theushw m4theushw merged commit 0203346 into mui:next Jan 6, 2023
@m4theushw m4theushw deleted the cell-outline branch January 6, 2023 14:41
@cherniavskii
Copy link
Member

I believe the focus and outline states are always synced in Spreadsheet.
Consider this example:

Screen.Recording.2023-01-06.at.16.04.01.mov

After Shift+Click the first cell is still outlined and focused. You can check that by navigating using the arrow keys - the focus will move from the cell that was outlined, not the one that was clicked with Shift.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change component: data grid This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[DataGrid] Cell consistency broken when keyboard event blur
4 participants