-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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: Try directing focus to the list view toggle button when closing the list view #54175
List View: Try directing focus to the list view toggle button when closing the list view #54175
Conversation
Size Change: +147 B (0%) Total Size: 1.62 MB
ℹ️ View Unchanged
|
Flaky tests detected in 22f6851. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6244591400
|
This approach is messy but makes sense. I cannot think of a better way to do this either. Do you have the ability to check if a block is not selected from edit-post package? Is there any cross-over between block-editor/edit-post stores? |
Oh, thanks for taking an early look, Alex! My plan is to add a check to see if there are no blocks selected before attempting to shift focus, which I think we should be able to do. My next step is to see if there's a slightly neater way to handle it, but in principle I think we'll need to find some way to pass the reference to the element along to the list view sidebar as in this PR. Whichever way we go with, we'll likely then want to duplicate the approach in the site editor. Will let you know how I go! |
6e32ac2
to
0d5d016
Compare
Update: I think this PR is in a good place for testing / feedback now. I think it's working pretty well, but unfortunately I haven't been able to figure out a way to do this without a fair bit of prop drilling in order to get a reference to the list view toggle button. Very happy for any feedback if folks have other ideas for how to implement this! Currently this PR just applies to the post and site editors. If the approach seems okay, then I can also implement it in the widget editor, too, for consistency. |
@@ -259,6 +259,7 @@ export default function HeaderEditMode() { | |||
/* translators: button label text should, if possible, be under 16 characters. */ | |||
label={ __( 'List View' ) } | |||
onClick={ toggleListView } | |||
ref={ setListViewToggleElement } |
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.
Passing a useState
callback seems fine to me. It's a stable reference.
I was just wondering if it would cause some cognitive confusion for anyone in the future trying to access current
.
Another question might be: is listViewToggleElement
(the state value) a value that’s needed for rendering the component in which it lives, and would it trigger an unnecessary re-render if set?
This is all purely academic, and I'm only raising because I don't know myself! Something like this might be an alternative to useState
in layout for example.
const listViewToggleElementRef = useRef( null );
const setListViewToggleElementRef = useCallback( ( node ) => {
if ( node !== null ) {
listViewToggleElementRef.current = node;
}
}, [] );
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.
Thanks for taking a look @ramonjd, and good question! I mostly went with useState
for consistency with some of the existing places where we're using ref callbacks to store elements in state (e.g. setDropZoneElement here). In terms of trying to avoid confusion and whether or not current
is available, I think so long as the variable is named with Element
as its suffix instead of Ref
, it'll hopefully be clear enough — or at least consistent enough with the other usage in the repo. Did you have any instances in mind where folks might reach for current
and get tripped up?
In terms of re-renders, one of the benefits of going with state is that the consumer (list-view-sidebar.js
) is fairly straightforward in that we can place listViewToggleElement
in the dependency arrays of useCallback
s and know that those callbacks will always be using the appropriate current value, without having to do any further introspection.
Some of the feedback I've gotten on previous PRs that looked at using refs with elements were to prefer using state... looking the references for that up now (if you'll excuse the pun 😄), I think the linked docs were in the floating UI library, so was more to do with working with popovers (docs, and talked about in #43335), so might not be strictly speaking applicable here.
In any case, from the perspective of the list view sidebars, it feels a tiny bit neater to me if we're accessing the element directly in the component's props, rather than using a ref there, but happy to look into changing things if folks would prefer, especially if I've accidentally introduced re-renders in this PR where I shouldn't have!
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 appreciate the follow up explanation. Good to know there's a precedent. I've seen it in other libraries too, so TIL it's a thing for Gutenberg too. 🙇🏻
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.
This is working well for me, and I think satisfies the lack of focus. Thanks!
The test steps work as described in post and site editors 👍🏻 I'll defer to @alexstine for an expert opinion.
In my demo videos below I've focussed on some random element (the preview button).
Before
In trunk, opening the list view leaves the focus on the preview button after toggling it closed.
2023-09-19.12.37.34.mp4
After
In this PR, opening the list view moves the focus to the list view button after toggling it closed.
2023-09-19.12.38.22.mp4
I left a question about useState
as a ref callback.
This is indeed a bug. If a block is selected and you close the list view with the shortcut, focus moves to the toggle instead of the selected block. If this is too hard to fix, let's not try and we'll just ensure that closing the list view always results in placing focus on the trigger. Thanks. |
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.
This worked exactly as the steps describe in both the post and site editors. Assuming everything else is good, I think this LGTM!
Thanks for the feedback @alexstine! From a bit of manual testing, I think the focus loss when closing the list view via the keyboard shortcut is to do with the iframed editor canvas. This appears to cause the focus to be directed to the iframe instead of the element that the user was in within the editor canvas. If I force the editor to no longer be iframed by enabling custom fields in the editor preferences, I see that the focus is working as it used to. It does seem like it could be tricky to try to fix that for the iframed editor. Just to make sure I'm following along, is your idea that we could remove the existing calls to I'm happy to give that a try, but let me know if that's not quite what you meant and I can backtrack or revert any changes along the way. |
Update: in 8f25ba0 I've had a go at removing the Thanks again for the feedback and reviews, everyone! I've also rolled out the change to the widgets editor since there hasn't been any real negative feedback about the prop drilling so far. As always, happy to keep iterating 🙂 |
Second update: I've updated the e2e tests to reflect focus returning to the list view toggle button. |
c35e4ef
to
22f6851
Compare
This PR works great 👏🏻 It's perfect when using the ESC shortcut - once to deselect all blocks, once to close the list view, focus lands on the list view toggle, magic 🌟 Nevertheless, I was a bit annoyed on losing the block focus when using the ctrl option O shortcut. In trunk it's worse than this PR tho, the focus being .. nowhere. Can we make it that the keyboard shortcut returns the focs to where it was before? CC @jeryj ? |
I'm fine with whatever! I have no reference of what is "standard." I really like having a global shortcut to move focus back to whatever was focused within the block editor , and it worked really well as part of this exploration. @alexstine and I are looking at adding the last focused element + fallback to the block editor redux store, which would make a global shortcut easy to add. We'd love some feedback on the approach there! |
I am fine with either. If it is too hard to focus the last position, let's not. In a perfect world, we would only focus the trigger if no blocks were selected but happy to wait until later to implement that. For now, as long as focus is consistent, it will be a big improvement over focus loss. |
Thanks for the reviews, testing, and feedback, folks!
Yes, unfortunately I found it a bit of a rabbithole to investigate due to the presence of the iframed editor. Given the timing for the WP 6.4 feature freeze, I think it'd be better to merge this PR as-is for now as it improves the consistency of focus, and it's preferable to have the focus be somewhere useful than nowhere at all, as you mention.
That approach does look promising. Let's merge this PR in for now, and we can see if an approach using the last focus state from #54443 could be used in a follow-up. Thanks again! 🙇 |
…osing the list view (#54175) * List View: Try directing focus to the list view toggle button when closing the list view * Only focus if no blocks are selected * Also use focus logic for when the list view is closed via the keyboard shortcut * Try implementing in the site editor, too * Always return focus to the toggle whenever the list view is closed, roll out to widgets editor * Update e2e tests
I just cherry-picked this PR to the release/16.7 branch to get it included in the next release: 49b9cff |
What?
This is an exploration into #54165 to see if we can manually direct focus to the list view toggle button when the list view is closed.
For reference, the keyboard shortcut for closing the list view is alt+shift+o on Windows, and control+option+o on Mac.
Why?
As raised in #54165, it is confusing for the user that sometimes when closing the list view, focus is lost completely. This happens most often when the list view was opened via the keyboard shortcut and is then closed after blocks have been deselected. There could be circumstances that exist not covered by this PR, but the hope is that this PR will improve things when closing the list view.
How?
Try using state in editor's layout, and pass down a setter to the heading in order to set a ref for the toggle button. Then, pass down the state that's keeping track of the toggle button element to the list view sidebar.
This requires a bit of prop drilling, as well as duplication between the editors. Because the editors are fairly different, I couldn't quite think of a better way to do it. I'm very happy to try out ideas if folks have ideas for a better way to do this!
Note: this PR previously only shifted focus if no blocks are selected. Based on discussion in the PR comments, I've since updated it to always shift focus when the list view is closed, to hopefully achieve more consistency.
Testing Instructions
Note: if closing the list view via the keyboard shortcut while blocks are still selected, then the list view should close and focus won't be explicitly moved to the list view toggle button (but it might go there anywhere depending on other logic already in place).
Screenshots or screencast
The below screengrab demos selecting a block in the list view and using the escape key to deselect and then close the list view, with focus ending up on the list view toggle button. Then, the list view is opened using the keyboard shortcut, with the blocks deselected using the escape key, and then finally the list view is closed via the keyboard shortcut again, with focus ending up on the list view toggle button.
2023-09-18.16.41.16.mp4