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

No focusable buttons when first toolbar item is disabled #3232

Open
jeryj opened this issue Dec 19, 2023 · 2 comments
Open

No focusable buttons when first toolbar item is disabled #3232

jeryj opened this issue Dec 19, 2023 · 2 comments

Comments

@jeryj
Copy link

jeryj commented Dec 19, 2023

Current behavior

If the first button in a toolbar is disabled after rerender, it does not cause the data-active-item and roving tabindex to update, so the disabled button remains as the he data-active-item and the rest of the buttons have a tabindex=-1, so no button in the toolbar is able to be focused.

Steps to reproduce the bug

  1. Open sandbox: https://codesandbox.io/p/sandbox/inspiring-cherry-6xwpl2
  2. Tab to the toolbar
  3. Focus skips the toolbar, as the active item is still set as the now disabled button, which can't receive focus.

Expected behavior

The toolbar will rerender and allow focus to be placed on the next available non-disabled button.

Workaround

#3232 (comment)

Possible solutions

No response

@diegohaz
Copy link
Member

diegohaz commented Dec 19, 2023

Thanks for reporting, @jeryj!

I believe this is because Ariakit doesn't automatically shift focus to another composite item when the current one becomes disabled or is removed from the DOM. This is because elements might be temporarily removed from the DOM due to React Suspense, virtualization, and so on. Plus, the active item id might point to an item that's not yet rendered, but will be soon. I'm planning to introduce a new feature to handle removable elements in the near future.

However, we could likely build something for disabled items directly into the composite item component.

In the meantime, here are two alternatives that can be implemented in userland:

  • Use the accessibleWhenDisabled prop on toolbar items.

  • Implement logic within a custom ToolbarItem component that automatically sets the active id when the current item is active and becomes disabled:

    function ToolbarItem(props) {
      const toolbar = Ariakit.useToolbarContext()!;
    
      const defaultId = React.useId();
      const id = props.id ?? defaultId;
      const isActiveDisabled = toolbar.useState((state) => {
        return props.disabled && state.activeId === id;
      });
    
      React.useEffect(() => {
        if (!isActiveDisabled) return;
        toolbar.setActiveId(toolbar.next());
      }, [isActiveDisabled, toolbar]);
    
      return <Ariakit.ToolbarItem id={id} {...props} />;
    }

@jeryj
Copy link
Author

jeryj commented Dec 20, 2023

Thank you for the workaround ideas, @diegohaz! Those are super helpful.

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

No branches or pull requests

2 participants