Skip to content

Conversation

manuhabitela
Copy link
Collaborator

@manuhabitela manuhabitela commented Aug 6, 2025

Context

The document search bar in the top panel has a few issues:

  • we can't navigate after the search button when using the tab key using Ctrl+O. The navigation is stopped when arriving on the search input and it blocks users from accessing focusable elements appearing after the search bar, in the top panel (for example the share button)
  • we can't focus the search input when using the tab key after using Ctrl+O shortcut to navigate in the top panel. Arriving on the input with Tab instantly closes the input. Works great with Ctrl+F though so it's not such a blocking issue here, but still ;)
  • we can't use the Tab key to focus the search bar items (the "all pages" checkbox, the arrow items, the close button)

Proposed solution

The idea behind this PR is:

  • navigation is not blocked anymore, we can access items from the top panel that are after the search bar when using Ctrl+O to focus the top panel then Tab
  • navigation with Ctrl+O then Tab is now also working as expected to focus the search input
  • tabbing through items in the search bar works. We can toggle the "all pages" checkbox with keyboard only and focus the arrow and close buttons.
  • pressing escape sets back the focus correctly on the current widget if not coming from Ctrl+O nav ; otherwise it just focuses back the search button, allowing the user to continue their navigation in the top panel

Has this been tested?

I did not add any specific test ; the few current tests making searches still pass. Not sure taking time to carefully test behavior is a must add here, I can add some if you think we must :)

  • 👍 yes, I added tests to the test suite
  • 💭 no, because this PR is a draft and still needs work
  • 🙅 no, because this is not relevant here (maybe?)
  • 🙋 no, because I need help

Screenshots / Screencasts

Before:

searchbar.before.mp4

After:

search.mp4

@manuhabitela manuhabitela force-pushed the searchbar-a11y branch 3 times, most recently from aa0072c to 2f62f04 Compare August 12, 2025 14:59
@manuhabitela manuhabitela marked this pull request as ready for review August 12, 2025 14:59
@manuhabitela manuhabitela self-assigned this Aug 12, 2025
@georgegevoian georgegevoian self-requested a review August 12, 2025 18:21
@manuhabitela manuhabitela force-pushed the searchbar-a11y branch 2 times, most recently from c5a3079 to ae81a21 Compare August 13, 2025 08:34
@manuhabitela manuhabitela moved this from In Progress to Needs Internal Feedback in French administration Board Aug 13, 2025
@manuhabitela
Copy link
Collaborator Author

This is ready to review on my side :) @fflorent @hexaltation if you wanna see

@manuhabitela manuhabitela added the preview Launch preview deployment of this PR label Aug 13, 2025
@manuhabitela manuhabitela removed their assignment Aug 13, 2025
Copy link
Contributor

Deployed commit ae81a21b7fcbb2cb2a80f65733e1b2017c283808 as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-09-12T09:30:39.004Z)

)
),
dom.onKeyDown({
Escape$: () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is there a $ here?
Does it act like a regex or is it a typo for the key code Escape?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's a grainjs feature:

By default, registered keyboard events are stopped from bubbling with stopPropagation() and preventDefault(). If, however, you register a key with a "$" suffix (i.e. Enter$ instead of Enter), then the event is allowed to bubble normally.

Copy link
Collaborator Author

@manuhabitela manuhabitela Aug 14, 2025

Choose a reason for hiding this comment

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

But your question shows I should add a comment here explaining why we don't want to stopPropagation here, you are right it's not obvious 👀

edit: added a comment to clarify

Copy link
Contributor

Deployed commit 022983ba413ad2f69cfd892e1859634860a036bb as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-09-18T12:35:47.903Z)

@manuhabitela manuhabitela moved this from Needs Internal Feedback to Needs feedback in French administration Board Aug 19, 2025
@georgegevoian
Copy link
Contributor

Thanks for working on this @manuhabitela.

The Mod+g/Mod+Shift+G shortcuts no longer work for me while the search input is expanded.

@manuhabitela
Copy link
Collaborator Author

The Mod+g/Mod+Shift+G shortcuts no longer work for me while the search input is expanded.

Woops, good catch thanks. I made a tiny change to make sure they work.

Copy link
Contributor

github-actions bot commented Sep 2, 2025

Deployed commit c22ce574a3daf940aad9dfb6acbf058e5fc71fc3 as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-10-02T12:41:33.929Z)

@georgegevoian
Copy link
Contributor

Thanks for the quick fix.

One other thing is that clicking the previous/next buttons or the search all pages checkbox no longer focuses the input. We have some tests not yet in this repo. Let me see about moving them here so you have more context.

@manuhabitela
Copy link
Collaborator Author

One other thing is that clicking the previous/next buttons or the search all pages checkbox no longer focuses the input. We have some tests not yet in this repo. Let me see about moving them here so you have more context.

That one I kinda removed intentionally, felt like sticking with usual browser behavior was a good thing part of the cleanup.

But I added it back with latest commit. This only applies when using the mouse in order to not mess up with keyboard nav where this behavior would be a bit weird :)

Copy link
Contributor

Deployed commit cfbebcc603200580431bd992f9fdc7cd356eb031 as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-10-30T16:53:21.722Z)

Copy link
Contributor

github-actions bot commented Oct 1, 2025

Deployed commit 0d2f84eaa7c43a21ca88ff74f29e33ac8c562d16 as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-10-31T07:36:31.894Z)

Copy link
Contributor

github-actions bot commented Oct 1, 2025

Deployed commit 785457a9633778ad4b7121a3e05cdaf0e9050ce4 as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-10-31T08:57:54.026Z)

@manuhabitela
Copy link
Collaborator Author

This should be better now!

I tried something in the last commit, what do you think? 785457a

@manuhabitela manuhabitela moved this from In Progress to Needs feedback in French administration Board Oct 1, 2025
@georgegevoian
Copy link
Contributor

LGTM. Thanks @manuhabitela!

I tried something in the last commit, what do you think? 785457a

Ok with me. Let me check with the rest of the team.

@georgegevoian
Copy link
Contributor

@manuhabitela there's one subtle regression, I think.

Before, if you had some cells selected it a table when you started searching, you could remove the selection after finishing searching by hitting Escape once more. Now, it doesn't do anything, and the selection remains until you paste it somewhere.

these few added methods will help outside code react to the current
region changing.

From outside the RFS, we can now:

- change the current region by simply giving a region id (no need to
understand the internal structure of the state object)
- listen to current region id changes
- get the current region id
trapTabKey internally uses a really helpful function to determine if a
given element is actually focusable or not (by checking not only the tag
or attributes used on the el, but also if it's visible or not).

this will be useful to have as a stand-alone util rather than it being
kinda hidden in the trapTabKey code.
There was a bug in the RFS code. Let's say:

- you focus the top panel with Ctrl+O
- you tab through items and end up focused on a button, lets say "share"
- you switch current region by clicking the main widget
- the "share" button gets hidden for whatever reason. Still in the dom,
but invisible
- you use Ctrl+O again to go back on the top panel

There, there was an error: we tried to focus back an unfocusable element
because it was no longer visible.

Now we correctly check if we can actually focus the element before
focusing it, otherwise we focus back the panel element itself.
before:

- we couldn't focus the search input when using the tab key after using
Ctrl+O shortcut to navigate in the top panel
- we couldn't navigate after the search button when using the tab key
after using Ctrl+O. The navigation was stopped when arriving on the
search input and it blocked users from accessing focusable elements
appearing after the search bar, in the top panel (for example the share
button)
- we couldn't use the Tab key to focus the search bar items (the "all
pages" checkbox, the arrow items, the close button)

Now:

- nav is not blocked anymore, we can access items after the searchbar
when using Ctrl+O to focus the top panel (most blocking issue)
- nav with Ctrl+O then Tab is working as expected to focus the search
input
- tabbing through items in the search bar works, we can toggle the "all
pages" checkbox with keyboard only and focus the arrow and close buttons
as they were before, we make sure those are attached on the input itself
so that they work correctly, not the wrapper div
this reapplies previous behavior before changing searchbar keyboard
behavior, making sure to only do it when using mouse, in order to not
disturb keyboard/screen reader users
when checking "search on all pages", the searchbar closed when
switching page because it listened for the RFS changes and called
`toggleMenu(false)`.

Change the logic so that the searchbar stays open on page changes
if we detect that it was opened before, + add a custom outside clicks
listener to properly handle closing the searchbar when we dont want it
anymore.
This is already doable with escape… But. Before, we could just press Tab
to exit the searchbar easily. Now, pressing Tab correctly tabs through
items next to the searchbar (like the "search all" checkbox). So this
quick way of getting out the searchbar is gone.

I felt like exposing a "submit search" action through Mod+Enter was a
good idea, it seems it better fits the intent than "escaping" search
bar. But I guess this is a bit nitpicking, we could also do without
this?
@manuhabitela
Copy link
Collaborator Author

What would Grist do without you George! Sorry about that. Working on that right now.

hopefully this helps better understanding things
Copy link
Contributor

github-actions bot commented Oct 3, 2025

Deployed commit 28d89b62874ca7bb45628eaed5464a2b4e44e82e as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-11-02T12:23:48.606Z)

This is a workaround exposing an issue that can happen with keyboard
handling:

When the RegionFocusSwitcher (RFS) focuses the ViewLayout, the
ViewLayout registers its commands again, making those commands the
"current" commands attached to the keyboard shortcuts.

So, if before focusing the ViewLayout, we had registered commands for
stuff that we would like to actually keep as "current" even after
focusing back the ViewLayout, we are not happy, because they get
overriden by the ViewLayout commands.

Here we decide to manually handle the escape key of the ViewLayout so
that it's only registered when we detect a maximized leaf, to fix a
specific issue related to this.

The issue was:

- I select cells in a grids. cancel command is now clearSelection from
the GridView
- I then open the searchbar. RFS focuses top panel,
- I then press Esc. The searchbar closes, RFS focuses the ViewLayout,
making ViewLayout commands be the current ones, including the "cancel"
one
- I then press Esc again, expecting to clear my selection. I can't
because the current cancel command is the ViewLayout's, basically doing
nothing when not maximized.

Now:

- I select cells in a grids. cancel command is now clearSelection from
the GridView)
- I then open the searchbar. RFS focuses top panel,
- I then press Esc. The searchbar closes, RFS focuses the ViewLayout,
making ViewLayout commands be the current ones, except for the "cancel"
one, because it activates only when the view is maximized,
- I then press Esc again, expecting to clear my selection, and my
selection is correctly cleared.
@manuhabitela
Copy link
Collaborator Author

Sooo the issue was kind of tricky, I explained it in details in the commit message, I'm not sure it warrants a novel as comments in the code as it's rather specific.

I'm not that fond of this workaround but I feel it's okay as I don't think we should encounter too similar many problems in the future. And I'd say the fix is rather good as it makes more sense to register the view layout "cancel" command only when necessary anyway.

What do you think?

Copy link
Contributor

github-actions bot commented Oct 3, 2025

Deployed commit c3f57ef400e358d56a18a181fcc99bcce374bded as https://grist-manuhabitela-grist-core-searchbar-a11y.fly.dev (until 2025-11-02T12:30:29.998Z)

@georgegevoian
Copy link
Contributor

Sooo the issue was kind of tricky, I explained it in details in the commit message, I'm not sure it warrants a novel as comments in the code as it's rather specific.

I'm not that fond of this workaround but I feel it's okay as I don't think we should encounter too similar many problems in the future. And I'd say the fix is rather good as it makes more sense to register the view layout "cancel" command only when necessary anyway.

What do you think?

I'm ok with the workaround. Thanks for tracking down the issue and fixing it.

@georgegevoian georgegevoian merged commit e5fdb29 into gristlabs:main Oct 3, 2025
27 of 28 checks passed
@github-project-automation github-project-automation bot moved this from Needs feedback to Done in French administration Board Oct 3, 2025
@manuhabitela
Copy link
Collaborator Author

Thanks for merging George 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Accessibility gouv.fr preview Launch preview deployment of this PR

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants