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

Add "form" attribute to all components that render an <input>, <textarea> or <select>

Open tchak opened this issue 2 years ago • 4 comments

🙋 Feature Request

Add "form" attribute to all components that render an <input>, <textarea> or <select>.

🤔 Expected Behavior

Be able to set form attribute on the internal <input>, <textarea> or <select>.

😯 Current Behavior

form attribute is not exposed.

💁 Possible Solution

I have tried to use ref and set form attribute in an useEffect. In some cases I am also using data-testid to gain access to underlying DOM/input and set form attribute in an useEffect. Neither is a good solution.

Another solution is to not use internal <input> (not set name on the component) and always use an external hidden <input>.

🔦 Context

It is more practical some time to render form and fields in separate parts of the DOM. For example, in cases when I want each field to have its own form. I can still group fields with one fieldset and put forms outside while referencing them from the inputs.

tchak avatar Feb 24 '23 10:02 tchak

I nearly ripped my eyes out writing this but here's a workaround if you're in an urgent need of a fix like myself:

        ref={
          // Workaround for react-aria-components not supporting form attribute
          // See https://github.com/adobe/react-spectrum/issues/4117
          form
            ? (ref) => {
                if (!ref) {
                  return;
                }

                // A hidden section rendered by react-aria-components
                const nextSibling = ref.nextSibling;

                if (!nextSibling || !(nextSibling instanceof HTMLElement)) {
                  return;
                }

                // A hidden input rendered by react-aria-components
                const formElement = nextSibling.querySelector(`[name="${name}"]`);

                if (!formElement) {
                  return;
                }

                formElement.setAttribute('form', form);
              }
            : undefined
        }

wojtekmaj avatar Mar 01 '24 15:03 wojtekmaj

For components where you provide an Input you can access it through the ref to that element https://stackblitz.com/edit/vitejs-vite-28q1zf?file=package.json,src%2FApp.tsx&terminal=dev

For the others, I think we could consider exposing an inputRef on the component or adding it to the ref via a useImperativeHandle.

If we do expose an inputRef then we'll have to determine how to deal with components that are implemented with multiple inputs, such as DatePicker or ColorArea.

snowystinger avatar Mar 04 '24 00:03 snowystinger

Hey,

I used to rewire the name and form props with the react-aria hooks, but since this PR I switch to RAC and it works great (👏) except for this two uses cases :

  • As mentioned in this issue, sometimes placing an input somewhere in the DOM is necessary
  • But a more simple use cas is to track if the form inputs have been changed without controlled component

For the second use case, here is an example with a TextField, a NumberField and a Select.

I tried with the form attribute on the <Input /> element but it land on the "aria input version" not the hidden one. The form attribute only purpose, if I'm correct, is to associate the input to the given form and should solve both use case. I cannot find a way to be sure it won't impact assistive technology.

mxp-qk avatar Mar 20 '24 21:03 mxp-qk

Related https://github.com/adobe/react-spectrum/discussions/6262

I've taken to just reimplementing these hidden inputs myself. Here's an example, but keep in mind that this is only to be used in FormData. It doesn't replace the hidden inputs RAC adds for accessibility, e.g. for slider thumbs.

<input
  className="sr-only"
  type="checkbox"
  aria-hidden={true}
  tabIndex={-1}
  value={isSelected ? checkedValue : uncheckedValue}
  checked={!(required && !isSelected)}
  required={required}
  name={name}
  form={form}
  onChange={() => null}
/>

Cannot believe that the supposedly golden standard component library overlooks this.

vincerubinetti avatar Apr 28 '24 01:04 vincerubinetti