mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(input-password-toggle): improve screen reader announcements (#30885)
Issue number: resolves internal
---------
<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->
<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->
## What is the current behavior?
The `ion-input-password-toggle` button uses `role="switch"` with
`aria-checked`, causing screen readers like VoiceOver to announce both a
state ("On/Off") and an action ("Show/Hide password"). This results in
confusing, redundant output such as "On, Hide Password" or "Off, Show
Password".
## What is the new behavior?
The password toggle button now uses `aria-pressed` instead of
`role="switch"` with `aria-checked`. Screen readers announce the
action-based label ("Show password" or "Hide password") along with the
pressed state, and properly announce state changes when the button is
activated.
## Does this introduce a breaking change?
- [ ] Yes
- [X] No
<!--
If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->
## Other information
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
[Old
Preview](https://ionic-framework-git-main-ionic1.vercel.app/src/components/input-password-toggle/test/basic)
[New
Preview](https://ionic-framework-git-fw-6920-ionic1.vercel.app/src/components/input-password-toggle/test/basic)
Current dev build:
```
8.7.15-dev.11766421552.180757ca
```
This commit is contained in:
@@ -170,6 +170,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
*/
|
||||
@Watch('aria-checked')
|
||||
@Watch('aria-label')
|
||||
@Watch('aria-pressed')
|
||||
onAriaChanged(newValue: string, _oldValue: string, propName: string) {
|
||||
this.inheritedAttributes = {
|
||||
...this.inheritedAttributes,
|
||||
|
||||
@@ -126,9 +126,8 @@ export class InputPasswordToggle implements ComponentInterface {
|
||||
color={color}
|
||||
fill="clear"
|
||||
shape="round"
|
||||
aria-checked={isPasswordVisible ? 'true' : 'false'}
|
||||
aria-label={isPasswordVisible ? 'Hide password' : 'Show password'}
|
||||
role="switch"
|
||||
aria-pressed={isPasswordVisible ? 'true' : 'false'}
|
||||
type="button"
|
||||
onPointerDown={(ev) => {
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
});
|
||||
|
||||
test.describe(title('input password toggle: aria attributes'), () => {
|
||||
test('should inherit aria attributes to inner button on load', async ({ page }) => {
|
||||
test('should have correct aria attributes on load', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="input" type="password">
|
||||
@@ -35,9 +35,9 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
const nativeButton = page.locator('ion-input-password-toggle button');
|
||||
|
||||
await expect(nativeButton).toHaveAttribute('aria-label', 'Show password');
|
||||
await expect(nativeButton).toHaveAttribute('aria-checked', 'false');
|
||||
await expect(nativeButton).toHaveAttribute('aria-pressed', 'false');
|
||||
});
|
||||
test('should inherit aria attributes to inner button after toggle', async ({ page }) => {
|
||||
test('should update aria attributes after toggle', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="input" type="password">
|
||||
@@ -51,7 +51,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await nativeButton.click();
|
||||
|
||||
await expect(nativeButton).toHaveAttribute('aria-label', 'Hide password');
|
||||
await expect(nativeButton).toHaveAttribute('aria-checked', 'true');
|
||||
await expect(nativeButton).toHaveAttribute('aria-pressed', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user