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

List view: Modify the shortcut to focus while open #45135

Merged
merged 22 commits into from Feb 14, 2023

Conversation

alexstine
Copy link
Contributor

@alexstine alexstine commented Oct 20, 2022

What?

Fixes #29466

This PR does the following.

  1. Changes the shortcut to focus the list view when the list view is open and focus is not inside the sidebar.
  2. When the list view is open and focus is inside the sidebar, using the shortcut will close it as normal.

Why?

See the related issue for more information. This allows the list view to remain visible for the sighted and still allows keyboard users to navigate back to the list view without closing and re-opening.

How?

It is not a pretty solution but it works. I left comments in the code to guide. The rest is magic.

Testing Instructions

  1. This is only in the post editor for now so open the post editor.
  2. Use the list view shortcut, on Windows, it is alt+shift+o.
  3. Notice how the list view sidebar opens and focus is placed inside.
  4. Press the shortcut again.
  5. Notice how focus went back to the position from which you opened the list view.
  6. Press the shortcut again and list view will open.
  7. Select a block.
  8. Press the shortcut again.
  9. Notice how focus goes back to the currently selected block in the list view.

Additional thoughts

  • Bug: The useFocusReturn hook needs some refactoring or different usage for this. Since it attaches when the component mounts, it will not return focus to where the user expects if they have navigated around the list view for a while. I need some way to call the hook dynamically but my research is turning up nothing. Suggestions on how to fix this would be appreciated.

It seems it would be better to give the sidebars it's own keyboard shortcuts. That would make abstraction of future functionality easier as well. For now, I think I'll leave it be. Being able to pass things from parent to child would likely help but we're not setup for it now.

Screenshots or screencast

@alexstine alexstine added [Type] Enhancement A suggestion for improvement. Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). Needs Accessibility Feedback [Package] Edit Post packages/edit-post [a11y] Keyboard & Focus labels Oct 20, 2022
@alexstine alexstine self-assigned this Oct 20, 2022
@codesandbox
Copy link

codesandbox bot commented Oct 20, 2022

CodeSandbox logoCodeSandbox logo  Open in CodeSandbox Web Editor | VS Code | VS Code Insiders

@github-actions
Copy link

github-actions bot commented Oct 20, 2022

Size Change: +153 B (0%)

Total Size: 1.33 MB

Filename Size Change
build/block-editor/index.min.js 193 kB +8 B (0%)
build/edit-post/index.min.js 34.6 kB +145 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 993 B
build/annotations/index.min.js 2.78 kB
build/api-fetch/index.min.js 2.27 kB
build/autop/index.min.js 2.15 kB
build/blob/index.min.js 483 B
build/block-directory/index.min.js 7.2 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 4.11 kB
build/block-editor/content.css 4.1 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/style-rtl.css 14.4 kB
build/block-editor/style.css 14.4 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 91 B
build/block-library/blocks/avatar/style.css 91 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 587 B
build/block-library/blocks/button/editor.css 587 B
build/block-library/blocks/button/style-rtl.css 628 B
build/block-library/blocks/button/style.css 627 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 100 B
build/block-library/blocks/categories/style.css 100 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 612 B
build/block-library/blocks/cover/editor.css 613 B
build/block-library/blocks/cover/style-rtl.css 1.57 kB
build/block-library/blocks/cover/style.css 1.56 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 265 B
build/block-library/blocks/file/style.css 265 B
build/block-library/blocks/file/view.min.js 353 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 984 B
build/block-library/blocks/gallery/editor.css 988 B
build/block-library/blocks/gallery/style-rtl.css 1.55 kB
build/block-library/blocks/gallery/style.css 1.55 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 830 B
build/block-library/blocks/image/editor.css 829 B
build/block-library/blocks/image/style-rtl.css 652 B
build/block-library/blocks/image/style.css 652 B
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/latest-comments/style-rtl.css 298 B
build/block-library/blocks/latest-comments/style.css 298 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 507 B
build/block-library/blocks/media-text/style.css 505 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 716 B
build/block-library/blocks/navigation-link/editor.css 715 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.13 kB
build/block-library/blocks/navigation/editor.css 2.14 kB
build/block-library/blocks/navigation/style-rtl.css 2.22 kB
build/block-library/blocks/navigation/style.css 2.2 kB
build/block-library/blocks/navigation/view-modal.min.js 2.81 kB
build/block-library/blocks/navigation/view.min.js 447 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 376 B
build/block-library/blocks/page-list/editor.css 376 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 174 B
build/block-library/blocks/paragraph/editor.css 174 B
build/block-library/blocks/paragraph/style-rtl.css 279 B
build/block-library/blocks/paragraph/style.css 281 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 501 B
build/block-library/blocks/post-comments-form/style.css 501 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 134 B
build/block-library/blocks/post-excerpt/style.css 134 B
build/block-library/blocks/post-featured-image/editor-rtl.css 586 B
build/block-library/blocks/post-featured-image/editor.css 584 B
build/block-library/blocks/post-featured-image/style-rtl.css 318 B
build/block-library/blocks/post-featured-image/style.css 318 B
build/block-library/blocks/post-navigation-link/style-rtl.css 153 B
build/block-library/blocks/post-navigation-link/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 282 B
build/block-library/blocks/post-template/style.css 282 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 326 B
build/block-library/blocks/pullquote/style.css 325 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 458 B
build/block-library/blocks/query/editor.css 457 B
build/block-library/blocks/quote/style-rtl.css 213 B
build/block-library/blocks/quote/style.css 213 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 409 B
build/block-library/blocks/search/style.css 406 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 490 B
build/block-library/blocks/site-logo/editor.css 490 B
build/block-library/blocks/site-logo/style-rtl.css 203 B
build/block-library/blocks/site-logo/style.css 203 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.4 kB
build/block-library/blocks/social-links/style.css 1.39 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 433 B
build/block-library/blocks/table/editor.css 433 B
build/block-library/blocks/table/style-rtl.css 651 B
build/block-library/blocks/table/style.css 650 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 404 B
build/block-library/blocks/template-part/editor.css 404 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 179 B
build/block-library/blocks/video/style.css 179 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 11.6 kB
build/block-library/editor.css 11.6 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 200 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 12.6 kB
build/block-library/style.css 12.6 kB
build/block-library/theme-rtl.css 698 B
build/block-library/theme.css 703 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.83 kB
build/blocks/index.min.js 51 kB
build/components/index.min.js 206 kB
build/components/style-rtl.css 11.6 kB
build/components/style.css 11.7 kB
build/compose/index.min.js 12.3 kB
build/core-data/index.min.js 15.9 kB
build/customize-widgets/index.min.js 11.8 kB
build/customize-widgets/style-rtl.css 1.41 kB
build/customize-widgets/style.css 1.41 kB
build/data-controls/index.min.js 663 B
build/data/index.min.js 8.57 kB
build/date/index.min.js 40.4 kB
build/deprecated/index.min.js 518 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.71 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/style-rtl.css 7.5 kB
build/edit-post/style.css 7.5 kB
build/edit-site/index.min.js 64.5 kB
build/edit-site/style-rtl.css 10 kB
build/edit-site/style.css 10 kB
build/edit-widgets/index.min.js 16.9 kB
build/edit-widgets/style-rtl.css 4.52 kB
build/edit-widgets/style.css 4.52 kB
build/editor/index.min.js 45.5 kB
build/editor/style-rtl.css 3.54 kB
build/editor/style.css 3.53 kB
build/element/index.min.js 4.93 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.26 kB
build/format-library/style-rtl.css 557 B
build/format-library/style.css 556 B
build/hooks/index.min.js 1.66 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.79 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.79 kB
build/keycodes/index.min.js 1.92 kB
build/list-reusable-blocks/index.min.js 2.14 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.99 kB
build/notices/index.min.js 977 B
build/plugins/index.min.js 1.95 kB
build/preferences-persistence/index.min.js 2.23 kB
build/preferences/index.min.js 1.35 kB
build/primitives/index.min.js 960 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 942 B
build/react-i18n/index.min.js 702 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.75 kB
build/reusable-blocks/index.min.js 2.26 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 10.8 kB
build/server-side-render/index.min.js 2.09 kB
build/shortcode/index.min.js 1.52 kB
build/style-engine/index.min.js 1.53 kB
build/token-list/index.min.js 650 B
build/url/index.min.js 3.69 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 1.09 kB
build/warning/index.min.js 280 B
build/widgets/index.min.js 7.31 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.06 kB

compressed-size-action

// This only fires when the list view is open because of the conditional rendering. It is the same shortcut to open but that is defined as a global shortcut and only fires when the list view is closed.
useShortcut( 'core/edit-post/toggle-list-view', () => {
// If the list view has focus, we know it is safe to close.
if ( listViewHasFocus ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of a new state value, can we instead rely on listViewRef.current.contains( document.activeElement ) here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@youknowriad That looks better? Seems to work the same.

@talldan
Copy link
Contributor

talldan commented Oct 21, 2022

It works really well in my testing. It's nice how it takes advantage of the roving tab index, so you can select a block from list view, and then use the shortcut to move straight back to that block in List View.

I spotted one issue, which is that if the List view sidebar close button is focused then shortcut doesn't close the sidebar, instead it moves focus into the list view treegrid. I think that's because the ref is bound to an element that isn't the parent of the close button, so checking for the active element doesn't work.

Changing this ref would make the roving tab index thing I mentioned above not work quite so well, the close button would be the first tabbable element found when using the shortcut to open or move focus into list view. A workaround for that could be to bind two refs, one that's checked when closing list view, the other that's used to move focus into list view when opening.

It adds more complexity to the code, but it may be the best option in terms of usability.

@alexstine
Copy link
Contributor Author

@talldan How does a solution like that look? This will need a new E2E test to make sure none of this breaks but it seems to have gotten me around the bug.

Thanks for reviewing.

@alexstine
Copy link
Contributor Author

Okay, added a fairly complex E2E for this. It should catch mistakes but this also helped me discover one other bug.

Selecting the already selected block in the list view will not trigger focus. This is likely related to useCallback deps? I'll fix it in a follow-up.

Thanks.

@alexstine alexstine added [Package] E2E Test Utils /packages/e2e-test-utils [Package] E2E Tests /packages/e2e-tests labels Oct 23, 2022
listViewRef.current.ownerDocument.activeElement
)
listViewRef.current
.closest( '[role="region"]' )
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this change needed to make sure the "close" button is within this div?

Can we instead just move the listViewRef to the div in line 62 of this file? The goal is to avoid relying on something that is not rendered by this component itself

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@youknowriad I gave it a try and updated the E2E test. How does this look?

@youknowriad
Copy link
Contributor

I'm noticing a small issue when the post is empty (no blocks at all). I load the post editor, I hit the shortcut, first it opens the list view but then it's stuck I can't close it anymore.

@alexstine
Copy link
Contributor Author

I'll try to investigate this shortly. Going out of town for a while. Tried setting up Gutenberg local dev on my Windows machine, interested to see if maybe I can improve the experience if I happen to find bugs while I am away from my Windows and Mac working environments over the LAN. Not all of us are lucky enough to fire up Linux or have Mac, this should be interesting.

@youknowriad
Copy link
Contributor

@alexstine yeah, I have trouble working when I'm not on my regular setup as well.

My codesandbox PR might help here, we'll have to see in the long run.

@alexstine
Copy link
Contributor Author

@youknowriad I pushed a fix but that still leaves us with a problem. The useFocusOnMount hook does not work in a no blocks situation either because there is nothing to focus if the ListView has no tabbables.

Might be time to refactor this a bit and call useEffect to handle our focus situations in the sidebar. Hate to do it, but I need full visibility over the state. useEffect would be perfect for that since I can listen for state changes.

Thoughts?

@alexstine
Copy link
Contributor Author

Just removed the useFocusOnMount hook in favor of useEffect. This allows me to use a callback function to determine where focus should be placed. Seems to work okay.

I need to refresh the branch later today and make sure the label changes have not broken anything.

@alexstine
Copy link
Contributor Author

I think this is mostly good to go. It might be worth breaking up the E2E test a bit if others think that is necessary. If all looks good, I'll apply to other packages. I think I finally managed to fix all bugs.

@kevin940726
Copy link
Member

During the Multi-block selection › should multi-select in the ListView component with shift + up and down keys test, on line 819, we expect getSelectedFlatIndices() (whatever that means) to equal [2, 3], but it just returns 1. I'm not sure what it means though, but it seems related to this PR.

@alexstine
Copy link
Contributor Author

@talldan or @andrewserong Any ideas as to why the above test is failing?

@alexstine
Copy link
Contributor Author

Would be nice to get a review on this. Need some assistance debugging the test, it is more than I know how to do at this point.

CC @annezazu

@andrewserong
Copy link
Contributor

Thanks for the ping, apologies for the late reply, I must have missed the ping while catching up after AFK.

Any ideas as to why the above test is failing?

I walked through the failing test manually in the post editor and I think it's failing because of a change in this PR when the list view is opened. With this PR applied, it seems that the focus is always over the first block in the list view when it opens, rather than the block that is currently selected within the editor canvas.

For example, if I create a post containing four blocks, with the fourth block selected, if I open the list view, then on trunk the fourth block will be focused. However with this PR applied, it looks like focus is positioned at the first block in the list view.

Was that part of the changes in this PR intentional?

@alexstine
Copy link
Contributor Author

@andrewserong That is not supposed to happen. Maybe I am not sending focus through the component properly? Can you spare a few moments to help me make this stable? So close.

@andrewserong
Copy link
Contributor

I took another look through the code, and I'm wondering if the reason the focus is going elsewhere is because the ref is now attached to the parent document overview container, which has tabbable elements within it, whereas previously the ref was attached to the container for the list view. So on trunk, I think that meant that useFocusOnMount would find the first tabbable node within the list view, which I think means the currently selected block?

However now that the ref is a level higher, listViewFocusLocation.focus(); seems to set focus to the list view itself, rather than the first tabbable node within it? I'm wondering if we need to do another lookup within listViewFocusLocation, so possibly something like focus.tabbable.find( listViewFocusLocation ) and then grab the first element of that, and focus to it?

I'm a little stretched for time this week (I'm off for a public holiday tomorrow), but I should have a little time on Friday to take another look.

@alexstine
Copy link
Contributor Author

@andrewserong I could not reproduce the same. If I select the 2nd block in the list and open the list view, I land on the 2nd block in the list view. I added preventScroll: true option since useFocusOnMount uses it.

@github-actions
Copy link

github-actions bot commented Jan 31, 2023

Flaky tests detected in 7945440.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4057808775
📝 Reported issues:

@andrewserong
Copy link
Contributor

@alexstine I'm a bit stuck attempting to re-review this one, as in testing listViewFocusLocation always seems to be the first item in the list for me, rather than the currently selected block. It could be something to do with the environment I'm running, but it does seem consistent with the failing e2e test.

One thing that stands out to me after another dive into the code, is that over in the ListViewBlock component, when it's initially mounted it calls cellRef.current.focus on this line. As far as I can tell, prior to this PR, that's the behaviour that ensures the selected item is selected when the list view is initially opened. However, the logic in this PR searches for the first tabbable element within the list view container, which defaults to the first link, because that link has tabIndex set to 0. At least, from inspecting in dev tools, that seems to be what's going on, but I very well could be wrong!

My hunch at the moment is that the focus behaviour in this PR means that the cell ref's focus isn't being called, so the focus never gets placed to the selected block in the list view, and so it doesn't receive an updated tabIndex value.

What does this all mean for next steps?

I'm wondering if it would help if we looked into limiting the scope of what this PR deals with so that handleSidebarFocus is only responsible for handling focus changes when the list view is open and not focused. That way, we could continue to rely on the behaviour in trunk for when the list view is initially opened, which would preserve the feature where the list view is scrolled to the currently selected block.

Now that we've passed feature freeze for 6.2, I should have a little more time to dig in and play around with some of these ideas. If you're happy for me to, I can experiment in this PR. Otherwise, I'm also happy to open up a separate PR to try that out, as I don't want to overwrite any of your good work here!

Thanks again for your patience with all this, it'll be a really good enhancement to land.

@alexstine
Copy link
Contributor Author

@andrewserong Can you remind me which browser E2E runs in? I think it is Chrome? I generally develop in Firefox so that could be it. Let me record a video tomorrow of what my experience is and maybe you can spot something that I just do not get visually.

I think the reason this PR fell a little out of scope is because having duplicate focus everywhere just did not make much sense to me. I wanted to try to make it clear to anyone in the future exactly what managed focus when as allowing the useFocusOnMount hook to stay around would have caused confusion.

At least that is my take...

This whole solution really is not pretty from a code perspective.

Thanks.

@andrewserong
Copy link
Contributor

Can you remind me which browser E2E runs in?

The failing e2e test uses Puppeteer to run the tests, and Puppeteer is based on Chromium browser, so that very well could be a factor here.

Also, I know what you mean about dealing with the managed focus, it's pretty tricky for folks to follow along when logic is spread across components, or is happening in different places. It took me quite a while to spot the cellRef focus behaviour that happens inside the list view component, too. Hopefully if we can get everything working correctly and the tests passing, the path to tidying up the code might become clearer.

I'll continue to have a play around with this also, and I'll let you know what I find!

@andrewserong
Copy link
Contributor

Update: it's pretty messy, but I've had a go at hacking on a change based on this PR over in #47901, specifically within list-view-sidebar.js. It's very much a throwaway PR, so feel free to disregard it, but for the moment I've been playing with the following:

  • Use refs for the list view button and the outline button, so that we can focus to them explicitly rather than doing a lookup for tabbable elements.
  • Use a selector query (borrowing from internal behaviour in the list view component) to find the anchor tag for the selected block if a block is selected, and then focus to it.

In manual testing, that appears to resolve the issue I ran into, however I think the tests are still failing in a different way, so might not be a reliable solution. I mostly put up the code change just in case it sparks any ideas!

I have limited time tomorrow to look into this further, so if not tomorrow, then I can continue digging in on Monday.

@alexstine
Copy link
Contributor Author

alexstine commented Feb 9, 2023

@andrewserong I actually like the idea of doing less lookups so I removed that aspect from my PR. I still require a couple lookups but I have used more refs in my latest commit. I also made a video showing the difference between my PR and trunk. I think we'll likely have to update the E2E tests. I think I get what you are saying now.

Thoughts?

Thanks.

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

Thanks so much for adding the video @alexstine, it's super helpful hearing you talk through the behaviour of the feature, and it's also nice to hear your voice! You make a good point comparing how the feature works on trunk, and honing in on the changes of this PR.

After giving this PR a re-test, I think your latest refactor has fixed up the main difference between this branch and trunk, and the previously failing e2e test is now passing. While testing manually, it seems that this branch now has the same behaviour as trunk for me when initially opening the list view, either by clicking on the list view button or using the keyboard shortcut. So, I think the addition of the extra refs and calling find on listViewApplicationFocus seems to have fixed up any disparity there.

I think this PR is in a good place to land now. The feature as implemented is working correctly, the e2es are passing, and otherwise the existing behaviour when opening the list view appears to be the same between trunk and this PR. I imagine some of the inconsistency on trunk could be to do with when the cell ref within the list view sets focus. It seems like it might be occurring inconsistently over there, but that's an issue that could be investigated separately.

Great work again, this LGTM!

Copy link
Contributor

@tellthemachines tellthemachines left a comment

Choose a reason for hiding this comment

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

Thanks for persisting with this! It's working well for me on macOS with Chrome and Safari, although it doesn't work with VoiceOver, due to the keyboard shortcut on mac being ctrl+opt+o. That is an existing issue tracked by #11154, so definitely not a blocker for this PR.

I also checked that with the list view open, it's still possible to reach it from inside a block by Shift+Tabbing a couple of times (first Shift+Tab lands us in the block toolbar, second in the list view).

One thing that would be nice to look at in a follow-up: when moving focus from the canvas to the already open list view, as far as I can tell focus is always transferred to the block that was last focused in the list view. So, if I have a Paragraph in the list view, move focus to the canvas, and then press Enter to create a second Paragraph, if I then move focus back to the list view it lands on the first Paragraph. This is also existing behaviour: I can reproduce it on trunk when Shift+Tabbing into the open list view from the canvas.

@alexstine
Copy link
Contributor Author

@tellthemachines Good call out. I think we'll need to figure out how to change that in the ListView component itself. Right now, I am just placing focus on the block with the proper tabindex in the grid, magic.

I am ready to give this a try and see if anyone notices any issues. Over the next few days, I will try to get this going for the site editor.

Thanks all.

@alexstine alexstine merged commit 21f8cdb into trunk Feb 14, 2023
46 checks passed
@alexstine alexstine deleted the try/list-view-modify-shortcut-focus branch February 14, 2023 03:38
@github-actions github-actions bot added this to the Gutenberg 15.2 milestone Feb 14, 2023
abhi3315 pushed a commit to abhi3315/gutenberg that referenced this pull request Feb 14, 2023
* Modify shortcut to focus list view when it is open.

* Remove extra state and figure out activeElement from ref.

* Try a sneaky way of making shortcut work even on close button focus.

* Fix static checks.

* Modify selector.

* Export isListViewOpen function.

* Add E2E test for new functionality.

* Fix focus events now that list view sidebar contains outline tab.

* Update E2E.

* Update E2E.

* Do not render ListView if there are no blocks.

* Fix focus for no blocks.

* Fix focus on mount.

* Move functionality to callback function entirely.

* Fix E2E.

* Remove merge artifact.

* Fix tests.

* Prevent scroll on focus.

* Rework code.
abhi3315 pushed a commit to abhi3315/gutenberg that referenced this pull request Feb 14, 2023
* Modify shortcut to focus list view when it is open.

* Remove extra state and figure out activeElement from ref.

* Try a sneaky way of making shortcut work even on close button focus.

* Fix static checks.

* Modify selector.

* Export isListViewOpen function.

* Add E2E test for new functionality.

* Fix focus events now that list view sidebar contains outline tab.

* Update E2E.

* Update E2E.

* Do not render ListView if there are no blocks.

* Fix focus for no blocks.

* Fix focus on mount.

* Move functionality to callback function entirely.

* Fix E2E.

* Remove merge artifact.

* Fix tests.

* Prevent scroll on focus.

* Rework code.
WunderBart added a commit that referenced this pull request Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[a11y] Keyboard & Focus Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Feature] List View Menu item in the top toolbar to select blocks from a list of links. Needs Accessibility Feedback [Package] Block editor /packages/block-editor [Package] E2E Test Utils /packages/e2e-test-utils [Package] E2E Tests /packages/e2e-tests [Package] Edit Post packages/edit-post [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Persistent List View: Add a way to focus back to the sidebar via keyboard
6 participants