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

[EuiDataGrid] Fix multiple grid focus restoration issues #5530

Merged
merged 11 commits into from
Jan 12, 2022

Conversation

cee-chen
Copy link
Member

@cee-chen cee-chen commented Jan 11, 2022

Summary

This PR fixes 3 bugs with EuiDataGrid around restoring correct focus/keyboard navigation to the grid:

c06db86 and 7f031ac closes #5517

If the user scrolls the currently focused cell out of view (via mouse) and then attempts to tab back into the grid (via keyboard), the grid should intelligently focus into the first visible and interactive cell (instead of the entire grid becoming unfocusable).

refocus_grid.mp4

d29d938 closes #4923

Problem: If the user hides the current column via the column header's actions popover, the popover cannot focus back into the now-removed cell, and sets the current focus back to the top of the document
Fix: Focus should be manually set to the grid by targeting its first visible and interactive cell (which is not perfect, but at least does not strand the user and require them tab all the way back to the grid)

hide_column.mp4

8a035de closes #5476

Fixes keyboard focus index being incorrect after moving a column left or right via the header's actions popover.

move_column.mp4

Checklist

- [ ] Check against all themes for compatibility in both light and dark modes
- [ ] Checked in mobile

  • Checked in Chrome, Safari, Edge, and Firefox

- [ ] Props have proper autodocs and playground toggles
- [ ] Added documentation
- [ ] Checked Code Sandbox works for any docs examples

- [ ] Checked for breaking changes and labeled appropriately

  • Checked for accessibility including keyboard-only and screenreader modes
  • A changelog entry exists and is marked appropriately
  • Revert [REVERT] commit

- and use this to determine whether or not the entire grid should have a tabindex/be focusable, rather than a simple 'hasFocus' check which doesn't account for virtualization

- Add new mockFocusContext for easier testing for various tests that need to set a focus context

- EuiDataGridCell
  - DRY out an `this.isFocusedCell` method
  - Call `setIsFocusedCellInView` on mount and on unmount (`setFocusedCell` handles setting true/false on new cell focus)
  - Write new unit tests for componentWillUnmount
  - Clean up unnecessary uncovered function fallback for this.unsubscribeCell - we're already checking for a falsy value before calling it
…ntent

- see http://localhost:8030/#/tabular-content/data-grid-focus
…irtualized refs

- see https://react-window.vercel.app/#/api/FixedSizeGrid for `onItemsRendered` API
- this struck me as the quickest/easiest way to determine which virtualized cells are in view
- I opted to save this as a ref instead of as state as I didn't see the need to cause a rerender

- `focusFirstVisibleInteractiveCell` was split out to its own fn as it will shortly be used elsewhere to fix another focus bug in the grid
@cee-chen
Copy link
Member Author

@1Copenut Tagging you in for QA help, since you opened at least one of the issues I'm fixing in this PR and have familiarity with the first one. Let me know if you see any issues with the fixes!

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5530/

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

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

LGTM, tested with the modified focus.js and the main example. Also messed with some of the tests locally to ensure their effectiveness 👍 .

@cee-chen
Copy link
Member Author

@1Copenut going to go ahead with turning auto merge on this PR, but feel free to confirm that the issue you opened is resolved after (+ ping me if you find anything else)!

@cee-chen cee-chen enabled auto-merge (squash) January 11, 2022 21:25
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5530/

- gridItemsRendered will be falsey if rowCount is 0 and will cause an error on tab, so we should opt to simply leave focus on the grid body wrapper
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5530/

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

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

Changes L even more GTM; tested the focus example with rowCounter={0} with both non- & interactive headers, and played more with the main example.

@cee-chen
Copy link
Member Author

cee-chen commented Jan 12, 2022

Trevor just found an issue that I'll push up another bugfix for:

  1. Go to https://eui.elastic.co/pr_5530/#/tabular-content/data-grid-virtualization
  2. Click or tab to first data cell (not header cell)
  3. Scroll to bottom of grid (or enough so that cell is unmounted)
  4. Press tab then shift tab back to the grid
  5. Notice that while focus is returned to the sticky header, you cannot actually press arrow down from the sticky header into the data cells
  6. This is because the sticky header is a row index of -1 and pressing down tries to return it to row 0 (first row) which is no longer rendered

I'm going to have to adjust the branching logic a bit further to account for this. Questions on how we prefer to treat this:

  1. If row 0 is no longer visible/rendered, focus the first visible data cell instead of focusing the first header cell
  2. Add some extra logic when pressing arrow down on the header row to try and find the first visible row index and jump to that instead of to row 0

Do we have any preference between the two options @1Copenut @chandlerprall? While 2 is kind of appealing as being "more intelligent", I think 1 is probably more straightforward and less likely to lead to edge cases/shenanigans - WDYT?
EDIT: Actually now that I think about it more I'm leaning towards more 2 haha...

@1Copenut
Copy link
Contributor

I feel like the first option, focusing the first visible data cell, would be a good experience. I could arrow up once to get into the header row, and left, right, down to move through the virtualized grid cells as normal.

How difficult is it to detect if a cell is in view? User scrolling will leave us with inexact "stops" -- users might have half a cell in the visible frame when the stop scrolling, does that cell get "snapped" to just below the header row? I might be inventing problems that'll be addressed as code is updated.

@cee-chen
Copy link
Member Author

cee-chen commented Jan 12, 2022

After more testing, I'm going to go with option 2 because I actually realized the issue can be fully reproduced by an even simpler means:

  1. Go to https://eui.elastic.co/pr_5530/#/tabular-content/data-grid-virtualization
  2. Don't even click anything or tab into it, but scroll to the middle or bottom of the grid
  3. Click the first header cell or tab into it, and try to press arrow down
  4. Nothing happens

Fixing the arrow down key behavior on the sticky header will generally be a more robust fix IMO.

edit to add: we could also try to force row 0 into view with a manual scrollToItem call when pressing arrow down from the sticky header, but I kinda feel like that's scrolljacking and worse UX (see my second bullet point in #5517 (comment))

@cee-chen
Copy link
Member Author

@1Copenut sorry, totally forgot to address a few points in your comment:

I could arrow up once to get into the header row, and left, right, down to move through the virtualized grid cells as normal.

Just wanted to clarify that even with this new fix, users will still need to go all the way to the first row (row 0) to be able to up arrow key into the sticky header. Not sure there is any way around this and this is how it's always been UX-wise AFAICT. In theory the Home key should allow users to quickly jump to row 0.

How difficult is it to detect if a cell is in view?

We get this for free from react-window, they have an onItemsRendered API that feeds us back the current first visible row index (which I added a ref for in this PR).

User scrolling will leave us with inexact "stops" -- users might have half a cell in the visible frame when the stop scrolling, does that cell get "snapped" to just below the header row? I might be inventing problems that'll be addressed as code is updated.

Not inventing problems at all, this is an open issue (#3151) that I currently have a separate open PR for: #5515

One downside of the disparate modular PRs is that we don't get to see them altogether in action just yet. So currently this PR will still have the same issue where you can down arrow key from the sticky header into a cell that's just barely visible. Once this PR merges, I'll absolutely update that PR to test out the combined behavior and make sure that newly focused cells are always in view.

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5530/

Copy link
Contributor

@1Copenut 1Copenut left a comment

Choose a reason for hiding this comment

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

LGTM! I did a fair bit of random key pressing after this latest update. The focus handling and setting to the first row, first cell on virtualized scroll all feels smooth and intuitive. Thank you Constance!

@cee-chen
Copy link
Member Author

Going to merge this in now as I suspect I'll need gridItemsRendered for the upcoming 'cells should always be in view' fix. Feel free to ping if there are any code issues or change requests, I can absolutely address in a follow-up PR

@cee-chen cee-chen merged commit 8cb2c3f into elastic:main Jan 12, 2022
@cee-chen cee-chen deleted the datagrid/5517/4923/5476 branch January 12, 2022 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants