From 782060a26e381c8ca5138b140e51989ae9bf314c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 20 Apr 2023 18:13:30 +0100 Subject: [PATCH] ARIA Accessibility improvements (#10674) * Add missing aria-expanded attributes * Improve autoComplete for phone numbers & email addresses * Fix room summary card heading order * Fix missing label on timeline search field * Use appropriate semantic elements for dropdown listbox * Use semantic list elements in keyboard settings tab * Use semantic list elements in spotlight * Fix types and i18n * Improve types * Update tests * Add snapshot test --- res/css/views/dialogs/_SpotlightDialog.pcss | 5 + res/css/views/right_panel/_BaseCard.pcss | 3 +- .../views/right_panel/_RoomSummaryCard.pcss | 5 +- .../tabs/user/_KeyboardUserSettingsTab.pcss | 5 + src/components/views/auth/CountryDropdown.tsx | 1 + .../views/dialogs/spotlight/Option.tsx | 14 +- src/components/views/elements/Dropdown.tsx | 20 +- src/components/views/right_panel/BaseCard.tsx | 2 +- .../views/right_panel/RoomSummaryCard.tsx | 2 +- src/components/views/rooms/SearchBar.tsx | 3 + .../views/settings/account/EmailAddresses.tsx | 2 +- .../views/settings/account/PhoneNumbers.tsx | 2 +- .../tabs/room/SecurityRoomSettingsTab.tsx | 1 + .../tabs/user/AppearanceUserSettingsTab.tsx | 6 +- .../tabs/user/KeyboardUserSettingsTab.tsx | 8 +- .../views/spaces/QuickSettingsButton.tsx | 1 + .../spaces/SpaceSettingsVisibilityTab.tsx | 1 + src/i18n/strings/en_EN.json | 2 + .../views/dialogs/SpotlightDialog-test.tsx | 14 +- .../FilterDropdown-test.tsx.snap | 12 +- .../RoomSummaryCard-test.tsx.snap | 12 +- .../user/AppearanceUserSettingsTab-test.tsx | 37 ++ .../AppearanceUserSettingsTab-test.tsx.snap | 386 ++++++++++++++++++ .../KeyboardUserSettingsTab-test.tsx.snap | 224 +++++----- 24 files changed, 611 insertions(+), 157 deletions(-) create mode 100644 test/components/views/settings/tabs/user/AppearanceUserSettingsTab-test.tsx create mode 100644 test/components/views/settings/tabs/user/__snapshots__/AppearanceUserSettingsTab-test.tsx.snap diff --git a/res/css/views/dialogs/_SpotlightDialog.pcss b/res/css/views/dialogs/_SpotlightDialog.pcss index b9fa634825..c85d94bf45 100644 --- a/res/css/views/dialogs/_SpotlightDialog.pcss +++ b/res/css/views/dialogs/_SpotlightDialog.pcss @@ -155,6 +155,11 @@ limitations under the License. overflow-y: auto; padding: $spacing-16; + ul { + padding: 0; + margin: 0; + } + .mx_SpotlightDialog_section { > h4, > .mx_SpotlightDialog_sectionHeader > h4 { diff --git a/res/css/views/right_panel/_BaseCard.pcss b/res/css/views/right_panel/_BaseCard.pcss index 22720a99e0..5f700dfbf3 100644 --- a/res/css/views/right_panel/_BaseCard.pcss +++ b/res/css/views/right_panel/_BaseCard.pcss @@ -151,10 +151,11 @@ limitations under the License. margin-right: $spacing-12; } - > h1 { + > h2 { color: $tertiary-content; font-size: $font-12px; font-weight: 500; + margin: $spacing-12; } .mx_BaseCard_Button { diff --git a/res/css/views/right_panel/_RoomSummaryCard.pcss b/res/css/views/right_panel/_RoomSummaryCard.pcss index f75743037b..a138e332ce 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.pcss +++ b/res/css/views/right_panel/_RoomSummaryCard.pcss @@ -19,8 +19,9 @@ limitations under the License. text-align: center; margin-top: $spacing-20; - h2 { + h1 { margin: $spacing-12 0 $spacing-4; + font-weight: $font-semi-bold; } .mx_RoomSummaryCard_alias { @@ -30,7 +31,7 @@ limitations under the License. text-overflow: ellipsis; } - h2, + h1, .mx_RoomSummaryCard_alias { display: -webkit-box; -webkit-line-clamp: 2; diff --git a/res/css/views/settings/tabs/user/_KeyboardUserSettingsTab.pcss b/res/css/views/settings/tabs/user/_KeyboardUserSettingsTab.pcss index aa65e6d494..6f387380f2 100644 --- a/res/css/views/settings/tabs/user/_KeyboardUserSettingsTab.pcss +++ b/res/css/views/settings/tabs/user/_KeyboardUserSettingsTab.pcss @@ -16,6 +16,11 @@ limitations under the License. */ .mx_KeyboardUserSettingsTab .mx_SettingsTab_section { + ul { + margin: 0; + padding: 0; + } + .mx_KeyboardShortcut_shortcutRow, .mx_KeyboardShortcut { display: flex; diff --git a/src/components/views/auth/CountryDropdown.tsx b/src/components/views/auth/CountryDropdown.tsx index 83f6eca71a..a9061f6e5a 100644 --- a/src/components/views/auth/CountryDropdown.tsx +++ b/src/components/views/auth/CountryDropdown.tsx @@ -164,6 +164,7 @@ export default class CountryDropdown extends React.Component { searchEnabled={true} disabled={this.props.disabled} label={_t("Country Dropdown")} + autoComplete="tel-country-code" > {options} diff --git a/src/components/views/dialogs/spotlight/Option.tsx b/src/components/views/dialogs/spotlight/Option.tsx index 2bfd2e17a1..a1dc41a852 100644 --- a/src/components/views/dialogs/spotlight/Option.tsx +++ b/src/components/views/dialogs/spotlight/Option.tsx @@ -15,18 +15,21 @@ limitations under the License. */ import classNames from "classnames"; -import React, { ComponentProps, ReactNode } from "react"; +import React, { ReactNode, RefObject } from "react"; -import { RovingAccessibleButton } from "../../../../accessibility/roving/RovingAccessibleButton"; import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex"; -import AccessibleButton from "../../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent } from "../../elements/AccessibleButton"; -interface OptionProps extends ComponentProps { +interface OptionProps { + inputRef?: RefObject; endAdornment?: ReactNode; + id?: string; + className?: string; + onClick: ((ev: ButtonEvent) => void) | null; } export const Option: React.FC = ({ inputRef, children, endAdornment, className, ...props }) => { - const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); return ( = ({ inputRef, children, endAdornment tabIndex={-1} aria-selected={isActive} role="option" + element="li" > {children}
diff --git a/src/components/views/elements/Dropdown.tsx b/src/components/views/elements/Dropdown.tsx index f603a4b957..305cee5196 100644 --- a/src/components/views/elements/Dropdown.tsx +++ b/src/components/views/elements/Dropdown.tsx @@ -30,7 +30,7 @@ interface IMenuOptionProps { highlighted?: boolean; dropdownKey: string; id?: string; - inputRef?: Ref; + inputRef?: Ref; onClick(dropdownKey: string): void; onMouseEnter(dropdownKey: string): void; } @@ -57,7 +57,7 @@ class MenuOption extends React.Component { }); return ( -
{ ref={this.props.inputRef} > {this.props.children} -
+ ); } } @@ -78,6 +78,7 @@ export interface DropdownProps { label: string; value?: string; className?: string; + autoComplete?: string; children: NonEmptyArray; // negative for consistency with HTML disabled?: boolean; @@ -318,21 +319,21 @@ export default class Dropdown extends React.Component { }); if (!options?.length) { return [ -
+
  • {_t("No results")} -
  • , + , ]; } return options; } public render(): React.ReactNode { - let currentValue; + let currentValue: JSX.Element | undefined; const menuStyle: CSSProperties = {}; if (this.props.menuWidth) menuStyle.width = this.props.menuWidth; - let menu; + let menu: JSX.Element | undefined; if (this.state.expanded) { if (this.props.searchEnabled) { currentValue = ( @@ -340,6 +341,7 @@ export default class Dropdown extends React.Component { id={`${this.props.id}_input`} type="text" autoFocus={true} + autoComplete={this.props.autoComplete} className="mx_Dropdown_option" onChange={this.onInputChange} value={this.state.searchQuery} @@ -355,9 +357,9 @@ export default class Dropdown extends React.Component { ); } menu = ( -
    +
      {this.getMenuOptions()} -
    + ); } diff --git a/src/components/views/right_panel/BaseCard.tsx b/src/components/views/right_panel/BaseCard.tsx index 615792057d..80e538c048 100644 --- a/src/components/views/right_panel/BaseCard.tsx +++ b/src/components/views/right_panel/BaseCard.tsx @@ -47,7 +47,7 @@ interface IGroupProps { export const Group: React.FC = ({ className, title, children }) => { return (
    -

    {title}

    +

    {title}

    {children}
    ); diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index a32d8da047..860a58df02 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -318,7 +318,7 @@ const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose }) />
    - {(name) =>

    {name}

    }
    + {(name) =>

    {name}

    }
    {alias}
    diff --git a/src/components/views/rooms/SearchBar.tsx b/src/components/views/rooms/SearchBar.tsx index c75b340c29..a4dbfe60ce 100644 --- a/src/components/views/rooms/SearchBar.tsx +++ b/src/components/views/rooms/SearchBar.tsx @@ -121,6 +121,9 @@ export default class SearchBar extends React.Component { type="text" autoFocus={true} placeholder={_t("Search…")} + aria-label={ + this.state.scope === SearchScope.Room ? _t("Search this room") : _t("Search all rooms") + } onKeyDown={this.onSearchChange} /> { { {this.state.showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")} diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 7a8157e38c..f4b05b3631 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -88,7 +88,11 @@ export default class AppearanceUserSettingsTab extends React.Component this.setState({ showAdvanced: !this.state.showAdvanced })}> + this.setState({ showAdvanced: !this.state.showAdvanced })} + aria-expanded={this.state.showAdvanced} + > {this.state.showAdvanced ? _t("Hide advanced") : _t("Show advanced")} ); diff --git a/src/components/views/settings/tabs/user/KeyboardUserSettingsTab.tsx b/src/components/views/settings/tabs/user/KeyboardUserSettingsTab.tsx index cf9a41a554..ef79b98d48 100644 --- a/src/components/views/settings/tabs/user/KeyboardUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/KeyboardUserSettingsTab.tsx @@ -41,10 +41,10 @@ const KeyboardShortcutRow: React.FC = ({ name }) => { if (!displayName || !value) return null; return ( -
    +
  • {displayName} -
  • + ); }; @@ -59,12 +59,12 @@ const KeyboardShortcutSection: React.FC = ({ cate return (
    {_t(category.categoryLabel)}
    -
    +
      {" "} {category.settingNames.map((shortcutName) => { return ; })}{" "} -
    +
    ); }; diff --git a/src/components/views/spaces/QuickSettingsButton.tsx b/src/components/views/spaces/QuickSettingsButton.tsx index 1adcd41960..4f9529466b 100644 --- a/src/components/views/spaces/QuickSettingsButton.tsx +++ b/src/components/views/spaces/QuickSettingsButton.tsx @@ -134,6 +134,7 @@ const QuickSettingsButton: React.FC<{ title={_t("Quick settings")} inputRef={handle} forceHide={!isPanelCollapsed} + aria-expanded={!isPanelCollapsed} > {!isPanelCollapsed ? _t("Settings") : null} diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index 7993a2af64..368d6c96fc 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -97,6 +97,7 @@ const SpaceSettingsVisibilityTab: React.FC = ({ matrixClient: cli, space onClick={toggleAdvancedSection} kind="link" className="mx_SettingsTab_showAdvanced" + aria-expanded={showAdvancedSection} > {showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")}
    diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a391253494..dd9afaf5d7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2141,6 +2141,8 @@ "This Room": "This Room", "All Rooms": "All Rooms", "Search…": "Search…", + "Search this room": "Search this room", + "Search all rooms": "Search all rooms", "Failed to connect to integration manager": "Failed to connect to integration manager", "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled", "Add some now": "Add some now", diff --git a/test/components/views/dialogs/SpotlightDialog-test.tsx b/test/components/views/dialogs/SpotlightDialog-test.tsx index e587ffbe5c..fb1fc1e65f 100644 --- a/test/components/views/dialogs/SpotlightDialog-test.tsx +++ b/test/components/views/dialogs/SpotlightDialog-test.tsx @@ -174,7 +174,7 @@ describe("Spotlight Dialog", () => { expect(filterChip.innerHTML).toContain("Public rooms"); const content = document.querySelector("#mx_SpotlightDialog_content")!; - const options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = content.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBe(1); expect(options[0].innerHTML).toContain(testPublicRoom.name); }); @@ -196,7 +196,7 @@ describe("Spotlight Dialog", () => { expect(filterChip.innerHTML).toContain("People"); const content = document.querySelector("#mx_SpotlightDialog_content")!; - const options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = content.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBeGreaterThanOrEqual(1); expect(options[0]!.innerHTML).toContain(testPerson.display_name); }); @@ -242,7 +242,7 @@ describe("Spotlight Dialog", () => { expect(filterChip.innerHTML).toContain("Public rooms"); const content = document.querySelector("#mx_SpotlightDialog_content")!; - const options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = content.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBe(1); expect(options[0]!.innerHTML).toContain(testPublicRoom.name); @@ -265,7 +265,7 @@ describe("Spotlight Dialog", () => { expect(filterChip.innerHTML).toContain("People"); const content = document.querySelector("#mx_SpotlightDialog_content")!; - const options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = content.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBeGreaterThanOrEqual(1); expect(options[0]!.innerHTML).toContain(testPerson.display_name); }); @@ -324,7 +324,7 @@ describe("Spotlight Dialog", () => { await flushPromisesWithFakeTimers(); const content = document.querySelector("#mx_SpotlightDialog_content")!; - options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + options = content.querySelectorAll("li.mx_SpotlightDialog_option"); }); it("should find Rooms", () => { @@ -350,7 +350,7 @@ describe("Spotlight Dialog", () => { jest.advanceTimersByTime(200); await flushPromisesWithFakeTimers(); - const options = document.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = document.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBeGreaterThanOrEqual(1); expect(options[0]!.innerHTML).toContain(testPerson.display_name); @@ -372,7 +372,7 @@ describe("Spotlight Dialog", () => { await flushPromisesWithFakeTimers(); const content = document.querySelector("#mx_SpotlightDialog_content")!; - const options = content.querySelectorAll("div.mx_SpotlightDialog_option"); + const options = content.querySelectorAll("li.mx_SpotlightDialog_option"); expect(options.length).toBe(1); expect(options[0].innerHTML).toContain(testPublicRoom.name); diff --git a/test/components/views/elements/__snapshots__/FilterDropdown-test.tsx.snap b/test/components/views/elements/__snapshots__/FilterDropdown-test.tsx.snap index d82ee64cef..1f1c5dff73 100644 --- a/test/components/views/elements/__snapshots__/FilterDropdown-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/FilterDropdown-test.tsx.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` renders dropdown options in menu 1`] = ` -
    -
    renders dropdown options in menu 1`] = ` Option one
    -
    -
    +
  • renders dropdown options in menu 1`] = ` with description
  • - - + + `; exports[` renders selected option 1`] = ` diff --git a/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap b/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap index 23f6e60087..ee66c73a68 100644 --- a/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap +++ b/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap @@ -44,11 +44,11 @@ exports[` renders the room summary 1`] = ` tabindex="0" /> -

    !room:domain.org -

    +
    renders the room summary 1`] = `
    -

    +

    About -

    +
    renders the room summary 1`] = `
    -

    +

    Widgets -

    +
    Room
    -
    +
      -
      Search (must be enabled) @@ -445,8 +445,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
      -
    -
    +
  • Upload a file @@ -471,8 +471,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • -
    -
    +
  • Dismiss read marker and jump to bottom @@ -485,8 +485,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • -
    -
    +
  • Jump to oldest unread message @@ -505,8 +505,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • -
    -
    +
  • Scroll up in the timeline @@ -519,8 +519,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • -
    -
    +
  • Scroll down in the timeline @@ -533,8 +533,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Jump to first message @@ -553,8 +553,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Jump to last message @@ -573,9 +573,9 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - + - +
    Room List
    -
    +
      -
      Select room from the room list @@ -600,8 +600,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
      -
    -
    +
  • Collapse room list section @@ -614,8 +614,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Expand room list section @@ -628,8 +628,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Navigate down in the room list @@ -642,8 +642,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Navigate up in the room list @@ -656,9 +656,9 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - + - +
    Accessibility
    -
    +
      -
      Close dialog or context menu @@ -683,8 +683,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
      -
    -
    +
  • Activate selected button @@ -697,9 +697,9 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - + - +
    Navigation
    -
    +
      -
      Toggle the top left menu @@ -730,8 +730,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
      -
    -
    +
  • Toggle right panel @@ -750,8 +750,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Toggle space panel @@ -776,8 +776,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Open this settings tab @@ -796,8 +796,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Go to Home View @@ -822,8 +822,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Jump to room search @@ -842,8 +842,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Next unread room or DM @@ -868,8 +868,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Previous unread room or DM @@ -894,8 +894,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Next room or DM @@ -914,8 +914,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Previous room or DM @@ -934,9 +934,9 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - + - +
    Autocomplete
    -
    +
      -
      Cancel autocomplete @@ -961,8 +961,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
      -
    -
    +
  • Next autocomplete suggestion @@ -975,8 +975,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Previous autocomplete suggestion @@ -989,8 +989,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Complete @@ -1003,8 +1003,8 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - -
    +
  • Force complete @@ -1017,9 +1017,9 @@ exports[`KeyboardUserSettingsTab renders list of keyboard shortcuts 1`] = `
  • - + - +