Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

ARIA Accessibility improvements #10674

Merged
merged 12 commits into from
Apr 20, 2023
5 changes: 5 additions & 0 deletions res/css/views/dialogs/_SpotlightDialog.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion res/css/views/right_panel/_BaseCard.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions res/css/views/right_panel/_RoomSummaryCard.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -30,7 +31,7 @@ limitations under the License.
text-overflow: ellipsis;
}

h2,
h1,
.mx_RoomSummaryCard_alias {
display: -webkit-box;
-webkit-line-clamp: 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/components/views/auth/CountryDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
searchEnabled={true}
disabled={this.props.disabled}
label={_t("Country Dropdown")}
autoComplete="tel-country-code"
>
{options}
</Dropdown>
Expand Down
14 changes: 9 additions & 5 deletions src/components/views/dialogs/spotlight/Option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof RovingAccessibleButton> {
interface OptionProps {
inputRef?: RefObject<HTMLLIElement>;
endAdornment?: ReactNode;
id?: string;
className?: string;
onClick: ((ev: ButtonEvent) => void) | null;
}

export const Option: React.FC<OptionProps> = ({ inputRef, children, endAdornment, className, ...props }) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLLIElement>(inputRef);
return (
<AccessibleButton
{...props}
Expand All @@ -36,6 +39,7 @@ export const Option: React.FC<OptionProps> = ({ inputRef, children, endAdornment
tabIndex={-1}
aria-selected={isActive}
role="option"
element="li"
>
{children}
<div className="mx_SpotlightDialog_option--endAdornment">
Expand Down
20 changes: 11 additions & 9 deletions src/components/views/elements/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface IMenuOptionProps {
highlighted?: boolean;
dropdownKey: string;
id?: string;
inputRef?: Ref<HTMLDivElement>;
inputRef?: Ref<HTMLLIElement>;
onClick(dropdownKey: string): void;
onMouseEnter(dropdownKey: string): void;
}
Expand All @@ -57,7 +57,7 @@ class MenuOption extends React.Component<IMenuOptionProps> {
});

return (
<div
<li
id={this.props.id}
className={optClasses}
onClick={this.onClick}
Expand All @@ -67,7 +67,7 @@ class MenuOption extends React.Component<IMenuOptionProps> {
ref={this.props.inputRef}
>
{this.props.children}
</div>
</li>
);
}
}
Expand All @@ -78,6 +78,7 @@ export interface DropdownProps {
label: string;
value?: string;
className?: string;
autoComplete?: string;
children: NonEmptyArray<ReactElement & { key: string }>;
// negative for consistency with HTML
disabled?: boolean;
Expand Down Expand Up @@ -318,28 +319,29 @@ export default class Dropdown extends React.Component<DropdownProps, IState> {
});
if (!options?.length) {
return [
<div key="0" className="mx_Dropdown_option" role="option" aria-selected={false}>
<li key="0" className="mx_Dropdown_option" role="option" aria-selected={false}>
{_t("No results")}
</div>,
</li>,
];
}
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 = (
<input
id={`${this.props.id}_input`}
type="text"
autoFocus={true}
autoComplete={this.props.autoComplete}
className="mx_Dropdown_option"
onChange={this.onInputChange}
value={this.state.searchQuery}
Expand All @@ -355,9 +357,9 @@ export default class Dropdown extends React.Component<DropdownProps, IState> {
);
}
menu = (
<div className="mx_Dropdown_menu" style={menuStyle} role="listbox" id={`${this.props.id}_listbox`}>
<ul className="mx_Dropdown_menu" style={menuStyle} role="listbox" id={`${this.props.id}_listbox`}>
{this.getMenuOptions()}
</div>
</ul>
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/views/right_panel/BaseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface IGroupProps {
export const Group: React.FC<IGroupProps> = ({ className, title, children }) => {
return (
<div className={classNames("mx_BaseCard_Group", className)}>
<h1>{title}</h1>
<h2>{title}</h2>
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/right_panel/RoomSummaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, permalinkCreator, onClose })
/>
</div>

<RoomName room={room}>{(name) => <h2 title={name}>{name}</h2>}</RoomName>
<RoomName room={room}>{(name) => <h1 title={name}>{name}</h1>}</RoomName>
<div className="mx_RoomSummaryCard_alias" title={alias}>
{alias}
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/components/views/rooms/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ export default class SearchBar extends React.Component<IProps, IState> {
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}
/>
<AccessibleButton
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/settings/account/EmailAddresses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
<Field
type="text"
label={_t("Email Address")}
autoComplete="off"
autoComplete="email"
disabled={this.state.verifying}
value={this.state.newEmailAddress}
onChange={this.onChangeNewEmailAddress}
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/settings/account/PhoneNumbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
<Field
type="text"
label={_t("Phone Number")}
autoComplete="off"
autoComplete="tel-national"
disabled={this.state.verifying}
prefixComponent={phoneCountry}
value={this.state.newPhoneNumber}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
onClick={this.toggleAdvancedSection}
kind="link"
className="mx_SettingsTab_showAdvanced"
aria-expanded={this.state.showAdvancedSection}
>
{this.state.showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")}
</AccessibleButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I

const brand = SdkConfig.get().brand;
const toggle = (
<AccessibleButton kind="link" onClick={() => this.setState({ showAdvanced: !this.state.showAdvanced })}>
<AccessibleButton
kind="link"
onClick={() => this.setState({ showAdvanced: !this.state.showAdvanced })}
aria-expanded={this.state.showAdvanced}
>
{this.state.showAdvanced ? _t("Hide advanced") : _t("Show advanced")}
</AccessibleButton>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ const KeyboardShortcutRow: React.FC<IKeyboardShortcutRowProps> = ({ name }) => {
if (!displayName || !value) return null;

return (
<div className="mx_KeyboardShortcut_shortcutRow">
<li className="mx_KeyboardShortcut_shortcutRow">
{displayName}
<KeyboardShortcut value={value} />
</div>
</li>
);
};

Expand All @@ -59,12 +59,12 @@ const KeyboardShortcutSection: React.FC<IKeyboardShortcutSectionProps> = ({ cate
return (
<div className="mx_SettingsTab_section" key={categoryName}>
<div className="mx_SettingsTab_subheading">{_t(category.categoryLabel)}</div>
<div>
<ul>
{" "}
{category.settingNames.map((shortcutName) => {
return <KeyboardShortcutRow key={shortcutName} name={shortcutName} />;
})}{" "}
</div>
</ul>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/components/views/spaces/QuickSettingsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const QuickSettingsButton: React.FC<{
title={_t("Quick settings")}
inputRef={handle}
forceHide={!isPanelCollapsed}
aria-expanded={!isPanelCollapsed}
>
{!isPanelCollapsed ? _t("Settings") : null}
</AccessibleTooltipButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
onClick={toggleAdvancedSection}
kind="link"
className="mx_SettingsTab_showAdvanced"
aria-expanded={showAdvancedSection}
>
{showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")}
</AccessibleButton>
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2138,6 +2138,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",
Expand Down
14 changes: 7 additions & 7 deletions test/components/views/dialogs/SpotlightDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand All @@ -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);
});
Expand Down Expand Up @@ -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);

Expand All @@ -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);
});
Expand Down Expand Up @@ -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", () => {
Expand All @@ -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);

Expand All @@ -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);

Expand Down
Loading