fix(a11y): accessibility audit - PortkeyDrop dialogs and main window#113
Merged
fix(a11y): accessibility audit - PortkeyDrop dialogs and main window#113
Conversation
Issues found and fixed across all dialogs and the main window: **SetName() misuse removed** - Removed SetName() calls on dialog self, panels, and most controls throughout app.py, host_key_dialog, properties, quick_connect, settings, site_manager, transfer, migration_dialog, update_dialog. SetName() sets an internal widget name not read by screen readers. **StaticText labels added for screen reader access** - app.py: Added "Local files:" and "Remote files:" StaticText labels before each wx.ListCtrl (the correct NVDA label mechanism for list controls). Removed the broken SetLabel/SetName calls on the lists themselves. - import_connections: Added "Configuration &path:" StaticText label before the path TextCtrl and wired SetLabelFor. - transfer: Added "Transfer Queue:" StaticText label before the transfer list. **SetLabelFor wired for programmatic label association** - properties: Each field label now calls lbl.SetLabelFor(val) to create an explicit accessible link between label and read-only value control. - quick_connect: Added _link() helper calling SetLabelFor for all fields. - site_manager: Added SetLabelFor in the fields loop replacing bare SetName. **Default buttons fixed** - host_key_dialog: Reject button is now the default (safest action for a security prompt); previously no default was set. - import_connections: Next/Import button is set as default per wizard step. - migration_dialog: "Migrate selected" button set as default. - quick_connect: OK button set as default via FindWindowById. - site_manager: Save button set as default. - transfer: Close button set as default. **Initial focus fixed** - host_key_dialog: Focus lands on Reject button (not the read-only detail TextCtrl) so screen readers announce the security decision immediately. - migration_dialog: Focus lands on the first checkbox. - quick_connect: Focus lands on protocol choice (first interactive field). - site_manager: Focus lands on the site list via wx.CallAfter. - transfer: Focus lands on the transfer list. - update_dialog: release_notes TextCtrl receives focus and insertion point is set to 0 so screen readers read from the top. **Keyboard navigation** - host_key_dialog: Escape now calls EndModal(REJECT) via EVT_CHAR_HOOK. - migration_dialog: Escape handler added via EVT_CHAR_HOOK. - site_manager: Escape handler added; Close button uses wx.ID_CANCEL. - update_dialog: SetEscapeId(wx.ID_CANCEL) set for standard Escape behaviour. - app.py: Ctrl+L now focuses tb_host when toolbar is visible, or remote_path_bar when toolbar is hidden, with an _announce() call. - transfer: Send-to-background returns focus to the parent window. **Button labels made more descriptive** - site_manager: Browse button → "&Browse for key file..." - transfer: "Retry" → "&Retry Selected Transfer", "Remove" → "&Remove Transfer" **Tests updated to match fixes** - Updated stubs in all affected test files (SetDefault, SetFocus, SetEscapeId, SetLabelFor, WXK_ESCAPE, EndModal, Bind). - Replaced test_dialog_sets_accessible_name (checked SetName) with test_dialog_title_is_accessible_name (checks dialog title). - Replaced test_initial_focus_is_security_text with test_initial_focus_is_reject_button. - Added _toolbar_panel MagicMock to _hydrate_frame in test_app.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit cde7dc6.
…ssue" This reverts commit 12aef4b.
Without this, pytest loaded portkeydrop from the main project's .pth installation instead of the worktree's src/, causing test_initial_focus_is_reject_button to load an older host_key_dialog.py that lacks reject_btn.SetDefault(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nnect is default button
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Comprehensive accessibility audit of all PortkeyDrop dialogs and the main window, targeting NVDA (Windows) and VoiceOver (macOS) screen reader compatibility.
Issues fixed
SetName() misuse removed
SetName()sets an internal widget name that screen readers cannot read. Removed all dialog-level and panel-levelSetName()calls acrossapp.py,host_key_dialog,properties,quick_connect,settings,site_manager,transfer,migration_dialog,update_dialog.StaticText labels added for wx.ListCtrl accessibility
StaticText. Added"Local files:"and"Remote files:"labels inapp.py, and"Transfer Queue:"intransfer.py. Removed the brokenSetLabel()/SetName()calls on the list controls themselves.SetLabelFor wired for programmatic label association
properties.py: Each field label now callslbl.SetLabelFor(val).quick_connect.py:_link()helper callsSetLabelForfor all form fields.site_manager.py:SetLabelForadded in the fields loop.import_connections.py: Path TextCtrl now has a visible label withSetLabelFor.Default buttons fixed (Enter key activation)
host_key_dialog: Reject button is now the default — safest action for a security prompt.import_connections: Next/Import button set as default per wizard step.migration_dialog: "Migrate selected" set as default.quick_connect: OK button set as default.site_manager: Save button set as default.transfer: Close button set as default.Initial focus
host_key_dialog: Focus lands on Reject button (not the read-only detail text) so screen readers immediately announce the security decision.migration_dialog: Focus on first checkbox.quick_connect: Focus on protocol choice (first interactive field).site_manager: Focus on site list viawx.CallAfter.transfer: Focus on transfer list.update_dialog: Focus +SetInsertionPoint(0)on release notes field.Keyboard navigation
host_key_dialog: Escape →EndModal(REJECT).migration_dialog: Escape handler viaEVT_CHAR_HOOK.site_manager: Escape handler; Close useswx.ID_CANCEL.update_dialog:SetEscapeId(wx.ID_CANCEL).app.py: Ctrl+L focusestb_host(toolbar visible) orremote_path_bar(toolbar hidden), with_announce()call.transfer: Send-to-background returns focus to the parent window.Descriptive button labels
"&Browse for key file...""&Retry Selected Transfer", Remove →"&Remove Transfer"Test plan
PYTHONPATH=src python -m pytest tests/ -q)SetDefault,SetFocus,SetEscapeId,SetLabelFor,WXK_ESCAPE,EndModal,Bindas needed)test_dialog_sets_accessible_name(was checkingSetName) withtest_dialog_title_is_accessible_nametest_initial_focus_is_security_textwithtest_initial_focus_is_reject_button_toolbar_panelMagicMock to_hydrate_frameintest_app.py🤖 Generated with Claude Code