react-spectrum icon indicating copy to clipboard operation
react-spectrum copied to clipboard

Allow disabled items to be focused

Open mlshv opened this issue 3 years ago • 11 comments

🙋 Feature Request

I am using useLink and useButton hooks from react-aria, but I've noticed that if those are disabled, element doesn't get focus. According to this thread on stackexachange:

If disabled buttons are not focusable with TAB, our user would occasionally miss that the button ever exists, and they will never find it.

So I'd like to be able to enable focus on disabled elements like links or buttons.

🔦 Context

There are situations when a screenreader user wouldn't know about a UI element which is disabled.

💻 Examples

Disabled button is not focusable: https://react-spectrum.adobe.com/react-aria/useButton.html

mlshv avatar Oct 19 '22 09:10 mlshv

Related https://github.com/adobe/react-spectrum/issues/1696

Can you tell us more about your use case?

One thing to note is that if you have a control that is focusable, it must meet contrast ratio requirements. It can be hard to design a control that both looks disabled and meets contrast requirements.

snowystinger avatar Oct 19 '22 16:10 snowystinger

I'm also affected by this, using Button from react-aria-components. I'm surprised that the types allow adding an aria-disabled prop but it doesn't show up on DOM.

I was implementing this technique to make soft disabled buttons, but it doesn't work since the prop is discarded.

My use case is similar to the one in the article, I want to disable the form submit button for a progressively enhanced submit is in progress, but don't want the user to lose focus.

frontsideair avatar Sep 30 '23 17:09 frontsideair

We suggest using something like https://spectrum.adobe.com/page/contextual-help/ (as mentioned here https://react-spectrum.adobe.com/react-aria/Tooltip.html#accessibility) to help users understand that there is a disabled button and why it is disabled. Tooltips are not a fully accessible way to share information (touch users cannot see it) and as such, shouldn't be used to convey critical information such as the criteria for a field to be valid.

You could also use form validation and inline messaging to convey this to all users. something like https://spectrum.adobe.com/page/help-text/

snowystinger avatar Oct 02 '23 19:10 snowystinger

It may be possible to use contextual help to give the same information aria-disabled would provide, but I have to check with a screen reader first. Validation wouldn’t help though, as my use case is not related to validity of field values, just the busy state and why the button can’t be pressed while it’s submitting. (I’m not considering using tooltips by the way, for the said reasons.)

frontsideair avatar Oct 03 '23 10:10 frontsideair

Ah, I see, the link you shared described a submit button which was disabled until the form was valid. But your actual use case is that the form is complete and you're actively submitting something. We just introduced this for React Spectrum components https://react-spectrum.adobe.com/react-spectrum/Button.html#pending I'm not sure if we have a plan to bring it over to RAC yet. Let me check with our team.

snowystinger avatar Oct 03 '23 10:10 snowystinger

I understand the confusion now, and I should've been more specific about the part I was referring to, there's a section that handles both incomplete and submitting states, and I thought aria-disabled would be good fit for this reason. (I don't use it for incomplete forms, relying on native constraint validation first and then server-side validation.)

Pending state for React Spectrum button seems perfect for my use case, as it seems to set aria-disabled and has a live region announcement. Can't wait for it to land on RAC too!

frontsideair avatar Oct 03 '23 13:10 frontsideair

You don't have to wait for it to land in RAC, you can create a RAC compatible button based on the hooks as described here https://react-spectrum.adobe.com/react-aria/Button.html#advanced-customization and you can include the little extra logic we incorporated into the Spectrum component there

snowystinger avatar Oct 08 '23 22:10 snowystinger

@snowystinger What's the status with having this for RAC? All we would need is to either allow aria-disabled to get passed for given component or having some prop like focusableWhenDisabled which would use aria-disabled instead of disabled when isDisabled prop is set.

joonass-visma avatar Jan 19 '24 08:01 joonass-visma

I have found a few tweaks to achieve "pending" like behaviour. Here's a stripped-back example, where we use isLoading as our pending indicator.

import { Button as RACButton } from "react-aria-components"
import clsx from "clsx"

export const Button = ({
  children,
  isDisabled = false,
  isLoading = false,
  onPress,
  type = "button",
}) => {
  const handlePress = (event) => {
    // Prevent usual press event when loading.
    if (!isLoading && onPress) {
      onPress(event)
    }
  }

  const isVisuallyDisabled = isLoading || isDisabled

  return (
    <RACButton
      className={clsx(isVisuallyDisabled ? "bg-gray-500" : "bg-blue-500")}
      isDisabled={
        // Override the disabled prop if loading, as this would circumvent
        // the efforts used to make this work correctly with screen-readers.
        !isLoading && isDisabled
      }
      onPress={handlePress}
      type={
        // Stop form submission when loading.
        isLoading ? "button" : type
      }
    >
      {isLoading ? <span>Loading</span> : children}
    </RACButton>
  )
}

Our tests are passing but please let me know if I missed something glaringly obvious 😖

will-stone avatar Aug 21 '24 13:08 will-stone

It's currently in progress RAC Pending Button

Note that in your example @will-stone that many screen reader/browser combinations will not read the updated label. It's not your fault, there is very poor support for this. Safari + VO will not announce anything.

We're still testing the PR, but hopefully it'll land soon and then you won't need to worry about things like that.

snowystinger avatar Aug 21 '24 23:08 snowystinger

You're right! I was testing in Firefox which worked perfectly. Even using aria-busy didn't work 😭 Well, at least my button keeps keyboard focus so... small wins 🤷‍♂️ Happy to see it's coming to RAC. Thank you 🙂

will-stone avatar Aug 22 '24 14:08 will-stone

We suggest using something like spectrum.adobe.com/page/contextual-help (as mentioned here react-spectrum.adobe.com/react-aria/Tooltip.html#accessibility) to help users understand that there is a disabled button and why it is disabled.

What would you suggest to do when it's about a toolbar of many bulk actions but which aren't all available for each selected item? Adding such an info icon to each action would simply be too much visual overload and also take too much space the more actions are added. Adding a single icon would then need to explain and link back to each individual button and why it's disabled.

I really try to adhere to the best practices set by React Aria but there are just some things where I don't know how to realistically do it.

levrik avatar Jan 09 '25 13:01 levrik

You could leave the button in place and not disabled but include a "warning icon" and when the action is clicked, it could display a dialog alerting the user why that action isn't currently possible.

Otherwise, display a message prominently saying something like "some actions are not available for the currently selected items" and then just leave those actions as disabled and non-focusable.

snowystinger avatar Jan 13 '25 00:01 snowystinger