adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
12.45k stars 1.08k forks source link

Allow disabled items to be focused #3662

Open mlshv opened 1 year ago

mlshv commented 1 year ago

πŸ™‹ 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

snowystinger commented 1 year ago

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.

frontsideair commented 11 months ago

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.

snowystinger commented 10 months ago

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/

frontsideair commented 10 months ago

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.)

snowystinger commented 10 months ago

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.

frontsideair commented 10 months ago

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!

snowystinger commented 10 months ago

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

joonass-visma commented 7 months ago

@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.

will-stone commented 5 days ago

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 πŸ˜–

snowystinger commented 5 days ago

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.

will-stone commented 4 days ago

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 πŸ™‚