Skip to content

[pm-41938] fix: Bank Account accessibility fixes - #2985

Open
morganzellers-bw wants to merge 8 commits into
pm-41934-bank-account-behavior-fixesfrom
pm-41938-voiceover-accessibility-fixes
Open

[pm-41938] fix: Bank Account accessibility fixes#2985
morganzellers-bw wants to merge 8 commits into
pm-41934-bank-account-behavior-fixesfrom
pm-41938-voiceover-accessibility-fixes

Conversation

@morganzellers-bw

@morganzellers-bw morganzellers-bw commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

  • PM-41938 - View Bank Account screen show/hide toggle has a generic button announcement
  • PM-41939 - Add & Edit Screen: show/hide toggle name announcement is not custom by line item
  • PM-41940 - Entered characters are not announced individually
  • PM-41941 - Overflow menu is not announced and can't be accessed
  • PM-41942 - MP Re-prompt information icon is not announced and can't be accessed

📔 Objective

Adds a batch of accessibility bug fixes under the Bank Account item type

📸 Screenshots

Click to reveal

PM-41938

showHideToggle.mov

PM-41939

addEditShowHideToggle.mov

PM-41940

individualCharacters.mov

PM-41941

overflowMenu.mov

PM-41942

mpRepromptInfoIcon.mov

@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context t:bug Change Type - Bug labels Aug 20, 2026
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 3.84615% with 25 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (pm-41934-bank-account-behavior-fixes@257c013). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...latform/Application/Views/BitwardenTextField.swift 5.88% 16 Missing ⚠️
BitwardenKit/Core/Platform/Extensions/String.swift 0.00% 5 Missing ⚠️
...rm/Application/Views/BitwardenTextValueField.swift 0.00% 4 Missing ⚠️
Additional details and impacted files
@@                           Coverage Diff                           @@
##             pm-41934-bank-account-behavior-fixes    #2985   +/-   ##
=======================================================================
  Coverage                                        ?   39.67%           
=======================================================================
  Files                                           ?      362           
  Lines                                           ?    16994           
  Branches                                        ?        0           
=======================================================================
  Hits                                            ?     6742           
  Misses                                          ?    10252           
  Partials                                        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@morganzellers-bw morganzellers-bw self-assigned this Aug 20, 2026
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from 92f03fd to 15f4a0d Compare August 25, 2026 17:11
@morganzellers-bw morganzellers-bw added ai-review Request a Claude code review and removed app:authenticator Bitwarden Authenticator app context labels Aug 26, 2026
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Re-reviewed the Bank Account VoiceOver batch against pm-41934-bank-account-behavior-fixes, focusing on the three commits added since the previous pass: the speechSpellsOutCharacters rework, the blank Account Type test, and the latest PasswordText spell-out change. The passwordVisibilityFieldName parameter is threaded through all four BitwardenTextField initializers and defaults to nil, so every existing caller keeps the generic "Password is/is not visible" wording; spellOutAccessibilityValue defaults to false on both PasswordText and BitwardenTextValueField, so only the six Bank Account fields opt in. The master password re-prompt info button now sits in BitwardenToggle's accessory slot with the toggle's accessibilityLabel passed explicitly, matching the existing Fill Assist pattern, and the vault row's accessibilityAsyncAction is correctly gated behind the same !isFromExtension/!isDecryptionFailure guard as the button it mirrors.

Code Review Details
  • ❓ : Re-added accessibilityValue alongside speechSpellsOutCharacters may reintroduce the double announcement that commit 70b20eb removed it to fix
    • BitwardenShared/UI/Platform/Application/Views/PasswordText.swift:29-32

@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from 15f4a0d to a6074fb Compare August 26, 2026 16:58
@github-actions github-actions Bot added the app:authenticator Bitwarden Authenticator app context label Aug 26, 2026
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from a6074fb to b68caa1 Compare August 27, 2026 14:24
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from b68caa1 to 04c13c5 Compare August 28, 2026 15:45
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from 388d472 to 7543515 Compare August 31, 2026 20:17
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from 7543515 to 1213f77 Compare August 31, 2026 20:23
// any character boundary, but that same text is used as the accessibility label by
// default. Override it with the zero-width-space-free password so VoiceOver doesn't
// announce the value once from the label and again from `speechSpellsOutCharacters`.
.accessibilityLabel(isPasswordVisible ? password : hiddenPassword)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QUESTION: Was the new accessibility label verified on the non-bank-account screens that also use PasswordText?

Details

colorCodedText(for:) inserts a zero-width space after every character, and until now that ZWSP-separated string was the implicit accessibility label for every visible PasswordText. This line replaces it with the raw value for all callers, but only the three bank-account fields opt into spellOutAccessibilityValue: true.

Callers that keep the default false and render visible values include:

  • ViewCardItemView.swift:47 / :104 — card number and security code
  • ViewLoginItemView.swift:59 — login password
  • ViewSSHKeyItemView.swift:32 — SSH private key
  • ViewPassportItemView.swift:141 / :186, ViewDriversLicenseItemView.swift:102, ViewIdentityItemView.swift:74
  • GeneratorView.swift:292, PasswordHistoryListView.swift:88, MasterPasswordGeneratorView.swift:48 — always visible

The ZWSP separators are commonly what pushes VoiceOver into per-character reading, so a numeric value like a card number or a generated password may now be announced as a single number/word rather than digit by digit on those screens. If that regression is real, passing spellOutAccessibilityValue: true at those call sites (or defaulting it to true) would keep the previous behavior.

@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from 71a508a to f6c8262 Compare September 2, 2026 20:10
…unt view toggles

VoiceOver announced the Account Number, PIN, and IBAN reveal toggles on the
Bank Account view screen with a generic "button" label because
PasswordVisibilityButton accepted an accessibilityLabel but never applied it.
Wires up field-specific announcements (e.g. "Account number is not visible,
tap to show.") for each toggle.
…unt add/edit toggles

VoiceOver announced the Account Number, PIN, and IBAN reveal toggles on the
Bank Account add/edit screen with the same generic "Password is/is not
visible" wording regardless of which field was being toggled. Adds an
optional field-name parameter to BitwardenTextField so these three fields
announce their own name (e.g. "PIN is visible, tap to hide.") while every
other password-style field in the app keeps its existing generic wording.
…o VoiceOver

VoiceOver was reading the Account Number, Routing Number, Branch Number, PIN,
SWIFT Code, and IBAN values on the Bank Account view screen as whole numbers
or words instead of spelling out each character, since no explicit
accessibilityValue was set for these fields. Adds an opt-in
spellOutAccessibilityValue flag to PasswordText and BitwardenTextValueField,
backed by a new String.spellingOutCharacters() helper, and wires it up only
for the six affected Bank Account fields so other consumers of these shared
components are unaffected.
…ows to VoiceOver

The row's more options button was visually tappable but unreachable by
VoiceOver because the row combines its content into a single
accessibility element, which swallows the nested button's own
announcement and activation. Adds a named accessibility action so the
overflow menu remains announced and accessible, matching the pattern
already used for PasswordHistoryListView's combined row.
…o icon to VoiceOver

The info button was nested inside BitwardenToggle's title content, so
VoiceOver collapsed the whole row into one element and swallowed the
button's own label and tap target. Move it into BitwardenToggle's
accessory slot, which keeps it independently reachable, and add an
"External link" hint, mirroring the Fill Assist toggle fix (PM-41097).
.accessibilityValue(value.spellingOutCharacters()) was being added on
top of a Text whose own content is already exposed as its
accessibility label, so VoiceOver would likely announce the value
twice: once normally, once spelled out. Use speechSpellsOutCharacters
instead, which tells VoiceOver to spell out the label itself rather
than adding a redundant value. Drops the now-unused
String.spellingOutCharacters() helper.

Also replaces a hand-rolled Task { await ... } in an
accessibilityAction with the existing accessibilityAsyncAction helper
for consistency.

ViewInspector can't inspect speechSpellsOutCharacters directly, so the
affected tests now verify the flag is passed through to PasswordText/
BitwardenTextValueField instead of the rendered accessibility value;
the actual VoiceOver announcement should be confirmed on-device.
The Add/Edit Bank Account screen's Account Type menu defaulted to
displaying "Checking" even when the user had never opened the picker,
because DefaultableType<BankAccountType>'s default-case label reused
the "Checking" option name instead of a blank placeholder.
@morganzellers-bw
morganzellers-bw force-pushed the pm-41938-voiceover-accessibility-fixes branch from f6c8262 to 663bb65 Compare September 2, 2026 21:32
Comment on lines +29 to +32
.if(spellOutAccessibilityValue && isPasswordVisible) { textView in
textView.accessibilityValue(password.spellingOutCharacters())
}
.speechSpellsOutCharacters(spellOutAccessibilityValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QUESTION: Was the duplicate-announcement behavior re-checked after re-adding accessibilityValue alongside speechSpellsOutCharacters?

Details

Commit 70b20eb removed .accessibilityValue(password.spellingOutCharacters()) for exactly this reason ("VoiceOver would likely announce the value twice: once normally, once spelled out") and replaced it with speechSpellsOutCharacters. The latest commit re-adds the value while keeping the modifier, so the element now has both a label (the rendered text) and a spelled-out value, with spell-out applied to both.

If on-device testing showed the modifier alone wasn't spelling out here — plausible, since colorCodedText(for:) inserts a zero-width space after every character — then this is the right fix and the question is just whether the value is now read once or twice. BitwardenTextValueField.swift:72 still uses the modifier alone, so the two components take different approaches.

Two smaller notes on the same lines:

  • speechSpellsOutCharacters is no longer gated on isPasswordVisible (it was in 70b20eb), so when a bank account field is masked the •••••••• placeholder is now spelled out one bullet at a time.
  • The replacingOccurrences(of: String.zeroWidthSpace, with: "") added to spellingOutCharacters() never fires on this path: password is the raw value, and the ZWSPs only exist in the AttributedString built by colorCodedText(for:).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context t:bug Change Type - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant