Search code examples
reactjsreact-selectreact-final-form

Change the order of the focused elements in final-form-focus


I'm using final-form-focus with react-final-form to focus on first error, if there are any validation errors on submit. I have a custom react-select component, so I'm using a custom findInput method like this:

const findInput = (inputs, errors) =>
  inputs.find(input => {
    const name = input.name || input.dataset.name;
    return name && getIn(errors, name);
  });

const getFormInputs = name => () => {
  if (typeof document === "undefined") {
    return [];
  }
  const form = document.forms[name];
  const inputs = form && form.length ? Array.prototype.slice.call(form) : []; // cast cheat to get from HTMLFormElement children to FocusableInput

  // So far this is just copy-pasted from final-form-focus.
  // It would return inputs here, but here is the magic:

  const selectFields = Array.prototype.slice.call(
    document.querySelectorAll("[data-select]")
  );

  const allInputs = inputs.concat(selectFields);

  return allInputs;
};

const focusOnError = focusDecorator(getFormInputs("pay-form"), findInput);

My select component is wrapped in a div and focused using ref like this:

const SelectInput = ({
  input,
  meta,
  initialValue,
  ...rest
}) => {
  const selectRef = React.createRef();

  const setFocus = () => {
    selectRef.current.focus();
  };
  return (
    <div>
      <div
        className="input-wrapper"
        tabIndex="0"
        data-select
        data-name={input.name}
        onFocus={setFocus}
      >
        <Select
          {...rest}
          ref={selectRef}
          onChange={value => input.onChange(value)}
          value={input.value}
          openMenuOnFocus={true}
        />
      </div>
    </div>
  );
};

But the select is only focused at the end of the line, after all other components. Here is my codesandbox. Is there any way to change the order of the focused elements in the same way as they appear in the document?


Solution

  • Looks like there's not a way to set the name attribute on the <input/> via React-Select, but you can set the id via innerId.

    Edit focus on error w/ react-select