Repository: JedWatson/react-select (ts)

Issue:
iOS Safari "tabbing" doesn't work for non-searchable Selects
iOS Safari displays a nifty little toolbar whenever an input element is focused, which lets you skip directly to the next (or previous) input on the page.

This works as expected for the plain Select:

<details>
<summary>
Working demo
</summary>

![demo of working navigation to searchable select](https://user-images.githubusercontent.com/1404650/50155843-6c4c4600-02cd-11e9-8023-f2324a212809.gif)

</details>

<br />

Setting `isSearchable={false}` has the side effect of making the `<input>` rendered by `<DummyInput>` `readonly`, which [removes it from the tab order in iOS Safari](https://stackoverflow.com/a/9811664):

<details>
<summary>
Broken demo
</summary>

![demo of broken navigation to non-searchable select](https://user-images.githubusercontent.com/1404650/50157611-3198dc80-02d2-11e9-8404-e419ef68c965.gif)

</details>

<br />

It's still in the tab order in desktop Safari, though, so… that's a thing that happens.

I tried working around this by avoiding the soft keyboard without the `readonly` attribute, but I couldn't do it, hence this issue.

Pull request:
Refactor how the DummyInput works when isSearchable={false}
Fixes #3291

Now that we've dropped IE11 we can use some newer platform features to achieve the same effect, but with a more accessible result.

The `readonly` attribute was added only when `isSearchable={false}` and achieved 2 things from what I've seen:

* hides the flashing cursor
* prevents devices from showing a virtual keyboard

We can solve these by:

* using the newer `caret-color` CSS prop and setting it to `transparent`
* set the `inputMode` attribute on the `<input>` to `none`

Tested on Chrome/Firefox for MacOS, and iOS 14 via the Simulator.

- 

Patch:
diff --git a/.changeset/lazy-rivers-camp.md b/.changeset/lazy-rivers-camp.md
new file mode 100644
index 00000000..d1b49628
--- /dev/null
+++ b/.changeset/lazy-rivers-camp.md
@@ -0,0 +1,5 @@
+---
+'react-select': patch
+---
+
+The readonly attribute has been removed from the DummyInput to improve accessibility
diff --git a/packages/react-select/src/Select.tsx b/packages/react-select/src/Select.tsx
index 24931efc..a7199d1c 100644
--- a/packages/react-select/src/Select.tsx
+++ b/packages/react-select/src/Select.tsx
@@ -1514,9 +1514,9 @@ export default class Select<
           onBlur={this.onInputBlur}
           onChange={noop}
           onFocus={this.onInputFocus}
-          readOnly
           disabled={isDisabled}
           tabIndex={tabIndex}
+          inputMode="none"
           form={form}
           value=""
           {...ariaAttributes}
diff --git a/packages/react-select/src/internal/DummyInput.tsx b/packages/react-select/src/internal/DummyInput.tsx
index 94f1310b..f6ce5741 100644
--- a/packages/react-select/src/internal/DummyInput.tsx
+++ b/packages/react-select/src/internal/DummyInput.tsx
@@ -17,6 +17,8 @@ export default function DummyInput({
         // get rid of any default styles
         background: 0,
         border: 0,
+        // important! this hides the flashing cursor
+        caretColor: 'transparent',
         fontSize: 'inherit',
         outline: 0,
         padding: 0,


Test patch:
diff --git a/packages/react-select/src/__tests__/Select.test.tsx b/packages/react-select/src/__tests__/Select.test.tsx
index 31211fa4..7c83e20a 100644
--- a/packages/react-select/src/__tests__/Select.test.tsx
+++ b/packages/react-select/src/__tests__/Select.test.tsx
@@ -2437,7 +2437,7 @@ cases(
   }
 );
 
-test('renders a read only input when isSearchable is false', () => {
+test('sets inputMode="none" when isSearchable is false', () => {
   let { container } = render(
     <Select
       classNamePrefix="react-select"
@@ -2454,7 +2454,10 @@ test('renders a read only input when isSearchable is false', () => {
   let input = container.querySelector<HTMLInputElement>(
     '.react-select__value-container input'
   );
-  expect(input!.readOnly).toBe(true);
+  expect(input!.inputMode).toBe('none');
+  expect(
+    window.getComputedStyle(input!).getPropertyValue('caret-color')
+  ).toEqual('transparent');
 });
 
 cases(