From 290088392b89d62ffa4acc7d6cbaa92617facd0f Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2024 16:27:15 -0400 Subject: [PATCH] fix(input): clear button can be navigated using screen reader (#29366) Issue number: resolves #29358 --------- ## What is the current behavior? When the clear button is focused, `focusin` is dispatched and bubbles up to the `ion-input`. Our [scroll assist utility listens for `focusin`](https://github.com/ionic-team/ionic-framework/blob/2fc81ddc9b35d6909fd4b585079aedabbd659233/core/src/utils/input-shims/hacks/scroll-assist.ts#L135) to adjust the scroll position. It also causes the input to be re-focused. As a result, when swiping to the clear button with a screen reader, focus will be forcibly moved back to the input. This means that users cannot swipe away from the input to the right when using a screen reader. ## What is the new behavior? - To fix this, I decided to have the `focusin` event from the clear button not bubble (as opposed to add a really specific workaround to the scroll assist utility). Adding `stopPropagation` was easy enough, but it turned out that the scroll assist listeners were all configured to listen during the capture phase instead of the bubble phase. As a result, `stopPropgation` had no effect because the scroll assist callback had already fired. To address this, I updated the listeners to listen during the bubbling phase instead of the capture phase. Based on my testing the capture phase was not required for scroll assist to work, so it appears safe to remove. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev build: `8.0.1-dev.11713535425.1a4afba3` Reviewers: Please test this on a physical iOS device and be sure to test the scroll assist behavior. There is a test at http://localhost:3333/src/utils/input-shims/hacks/test you can use. --- core/src/components/input/input.tsx | 9 +++++++++ core/src/utils/input-shims/hacks/scroll-assist.ts | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 3607b103221..b08160c1a28 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -831,6 +831,15 @@ export class Input implements ComponentInterface { */ ev.preventDefault(); }} + onFocusin={(ev) => { + /** + * Prevent the focusin event from bubbling otherwise it will cause the focusin + * event listener in scroll assist to fire. When this fires, focus will be moved + * back to the input even if the clear button was never tapped. This poses issues + * for screen readers as it means users would be unable to swipe past the clear button. + */ + ev.stopPropagation(); + }} onClick={this.clearTextInput} > diff --git a/core/src/utils/input-shims/hacks/scroll-assist.ts b/core/src/utils/input-shims/hacks/scroll-assist.ts index 01f810f3295..293f1d1d26d 100644 --- a/core/src/utils/input-shims/hacks/scroll-assist.ts +++ b/core/src/utils/input-shims/hacks/scroll-assist.ts @@ -124,7 +124,7 @@ export const enableScrollAssist = ( const focusOut = () => { hasKeyboardBeenPresentedForTextField = false; win?.removeEventListener('ionKeyboardDidShow', keyboardShow); - componentEl.removeEventListener('focusout', focusOut, true); + componentEl.removeEventListener('focusout', focusOut); }; /** @@ -155,15 +155,15 @@ export const enableScrollAssist = ( ); win?.addEventListener('ionKeyboardDidShow', keyboardShow); - componentEl.addEventListener('focusout', focusOut, true); + componentEl.addEventListener('focusout', focusOut); }; - componentEl.addEventListener('focusin', focusIn, true); + componentEl.addEventListener('focusin', focusIn); return () => { - componentEl.removeEventListener('focusin', focusIn, true); + componentEl.removeEventListener('focusin', focusIn); win?.removeEventListener('ionKeyboardDidShow', keyboardShow); - componentEl.removeEventListener('focusout', focusOut, true); + componentEl.removeEventListener('focusout', focusOut); }; };