Fix usages of ARIA tabpanel (#10628)

* RovingTabIndex handle looping around start/end

* Make TabbedView expose aria tabpanel/tablist/tab roles

* Fix right panel being wrongly specified as aria tabs

Not all right panels map to the top right header buttons so we cannot describe it as a tabpanel relation

* tsc strict

* Update snapshots

* Fix ARIA AXE violation

* Update tests
This commit is contained in:
Michael Telatynski 2023-04-25 09:31:54 +01:00 committed by GitHub
parent 961b843662
commit a1a087f755
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 153 additions and 66 deletions

View file

@ -59,7 +59,7 @@ const INTEGRATION_MANAGER_HTML = `
`; `;
function openIntegrationManager() { function openIntegrationManager() {
cy.findByRole("tab", { name: "Room info" }).click(); cy.findByRole("button", { name: "Room info" }).click();
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click(); cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
} }

View file

@ -62,7 +62,7 @@ const INTEGRATION_MANAGER_HTML = `
`; `;
function openIntegrationManager() { function openIntegrationManager() {
cy.findByRole("tab", { name: "Room info" }).click(); cy.findByRole("button", { name: "Room info" }).click();
cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click(); cy.findByRole("button", { name: "Add widgets, bridges & bots" }).click();
} }

View file

@ -156,6 +156,7 @@ export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction
}; };
interface IProps { interface IProps {
handleLoop?: boolean;
handleHomeEnd?: boolean; handleHomeEnd?: boolean;
handleUpDown?: boolean; handleUpDown?: boolean;
handleLeftRight?: boolean; handleLeftRight?: boolean;
@ -167,6 +168,7 @@ export const findSiblingElement = (
refs: RefObject<HTMLElement>[], refs: RefObject<HTMLElement>[],
startIndex: number, startIndex: number,
backwards = false, backwards = false,
loop = false,
): RefObject<HTMLElement> | undefined => { ): RefObject<HTMLElement> | undefined => {
if (backwards) { if (backwards) {
for (let i = startIndex; i < refs.length && i >= 0; i--) { for (let i = startIndex; i < refs.length && i >= 0; i--) {
@ -174,12 +176,18 @@ export const findSiblingElement = (
return refs[i]; return refs[i];
} }
} }
if (loop) {
return findSiblingElement(refs.slice(startIndex + 1), refs.length - 1, true, false);
}
} else { } else {
for (let i = startIndex; i < refs.length && i >= 0; i++) { for (let i = startIndex; i < refs.length && i >= 0; i++) {
if (refs[i].current?.offsetParent !== null) { if (refs[i].current?.offsetParent !== null) {
return refs[i]; return refs[i];
} }
} }
if (loop) {
return findSiblingElement(refs.slice(0, startIndex), 0, false, false);
}
} }
}; };
@ -188,6 +196,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
handleHomeEnd, handleHomeEnd,
handleUpDown, handleUpDown,
handleLeftRight, handleLeftRight,
handleLoop,
onKeyDown, onKeyDown,
}) => { }) => {
const [state, dispatch] = useReducer<Reducer<IState, IAction>>(reducer, { const [state, dispatch] = useReducer<Reducer<IState, IAction>>(reducer, {
@ -252,7 +261,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
handled = true; handled = true;
if (context.state.refs.length > 0) { if (context.state.refs.length > 0) {
const idx = context.state.refs.indexOf(context.state.activeRef!); const idx = context.state.refs.indexOf(context.state.activeRef!);
focusRef = findSiblingElement(context.state.refs, idx + 1); focusRef = findSiblingElement(context.state.refs, idx + 1, false, handleLoop);
} }
} }
break; break;
@ -266,7 +275,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
handled = true; handled = true;
if (context.state.refs.length > 0) { if (context.state.refs.length > 0) {
const idx = context.state.refs.indexOf(context.state.activeRef!); const idx = context.state.refs.indexOf(context.state.activeRef!);
focusRef = findSiblingElement(context.state.refs, idx - 1, true); focusRef = findSiblingElement(context.state.refs, idx - 1, true, handleLoop);
} }
} }
break; break;
@ -289,7 +298,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
}); });
} }
}, },
[context, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight], [context, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight, handleLoop],
); );
return ( return (

View file

@ -22,9 +22,9 @@ import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import AccessibleButton from "../views/elements/AccessibleButton";
import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers"; import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers";
import { NonEmptyArray } from "../../@types/common"; import { NonEmptyArray } from "../../@types/common";
import { RovingAccessibleButton, RovingTabIndexProvider } from "../../accessibility/RovingTabIndex";
/** /**
* Represents a tab for the TabbedView. * Represents a tab for the TabbedView.
@ -98,9 +98,10 @@ export default class TabbedView extends React.Component<IProps, IState> {
} }
private renderTabLabel(tab: Tab): JSX.Element { private renderTabLabel(tab: Tab): JSX.Element {
let classes = "mx_TabbedView_tabLabel "; const isActive = this.state.activeTabId === tab.id;
const classes = classNames("mx_TabbedView_tabLabel", {
if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active"; mx_TabbedView_tabLabel_active: isActive,
});
let tabIcon: JSX.Element | undefined; let tabIcon: JSX.Element | undefined;
if (tab.icon) { if (tab.icon) {
@ -108,24 +109,35 @@ export default class TabbedView extends React.Component<IProps, IState> {
} }
const onClickHandler = (): void => this.setActiveTab(tab); const onClickHandler = (): void => this.setActiveTab(tab);
const id = this.getTabId(tab);
const label = _t(tab.label); const label = _t(tab.label);
return ( return (
<AccessibleButton <RovingAccessibleButton
className={classes} className={classes}
key={"tab_label_" + tab.label} key={"tab_label_" + tab.label}
onClick={onClickHandler} onClick={onClickHandler}
data-testid={`settings-tab-${tab.id}`} data-testid={`settings-tab-${tab.id}`}
role="tab"
aria-selected={isActive}
aria-controls={id}
> >
{tabIcon} {tabIcon}
<span className="mx_TabbedView_tabLabel_text">{label}</span> <span className="mx_TabbedView_tabLabel_text" id={`${id}_label`}>
</AccessibleButton> {label}
</span>
</RovingAccessibleButton>
); );
} }
private getTabId(tab: Tab): string {
return `mx_tabpanel_${tab.id}`;
}
private renderTabPanel(tab: Tab): React.ReactNode { private renderTabPanel(tab: Tab): React.ReactNode {
const id = this.getTabId(tab);
return ( return (
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}> <div className="mx_TabbedView_tabPanel" key={id} id={id} aria-labelledby={`${id}_label`}>
<AutoHideScrollbar className="mx_TabbedView_tabPanelContent">{tab.body}</AutoHideScrollbar> <AutoHideScrollbar className="mx_TabbedView_tabPanelContent">{tab.body}</AutoHideScrollbar>
</div> </div>
); );
@ -147,7 +159,23 @@ export default class TabbedView extends React.Component<IProps, IState> {
return ( return (
<div className={tabbedViewClasses}> <div className={tabbedViewClasses}>
{screenName && <PosthogScreenTracker screenName={screenName} />} {screenName && <PosthogScreenTracker screenName={screenName} />}
<div className="mx_TabbedView_tabLabels">{labels}</div> <RovingTabIndexProvider
handleLoop
handleHomeEnd
handleLeftRight={this.props.tabLocation == TabLocation.TOP}
handleUpDown={this.props.tabLocation == TabLocation.LEFT}
>
{({ onKeyDownHandler }) => (
<div
className="mx_TabbedView_tabLabels"
role="tablist"
aria-orientation={this.props.tabLocation == TabLocation.LEFT ? "vertical" : "horizontal"}
onKeyDown={onKeyDownHandler}
>
{labels}
</div>
)}
</RovingTabIndexProvider>
{panel} {panel}
</div> </div>
); );

View file

@ -54,8 +54,7 @@ export default class HeaderButton extends React.Component<IProps> {
return ( return (
<AccessibleTooltipButton <AccessibleTooltipButton
{...props} {...props}
aria-selected={isHighlighted} aria-current={isHighlighted ? "true" : "false"}
role="tab"
title={title} title={title}
alignment={Alignment.Bottom} alignment={Alignment.Bottom}
className={classes} className={classes}

View file

@ -98,10 +98,6 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
public abstract renderButtons(): JSX.Element; public abstract renderButtons(): JSX.Element;
public render(): React.ReactNode { public render(): React.ReactNode {
return ( return <div className="mx_HeaderButtons">{this.renderButtons()}</div>;
<div className="mx_HeaderButtons" role="tablist">
{this.renderButtons()}
</div>
);
} }
} }

View file

@ -6,12 +6,16 @@ exports[`<TabbedView /> renders tabs 1`] = `
class="mx_TabbedView mx_TabbedView_tabsOnLeft" class="mx_TabbedView mx_TabbedView_tabsOnLeft"
> >
<div <div
aria-orientation="vertical"
class="mx_TabbedView_tabLabels" class="mx_TabbedView_tabLabels"
role="tablist"
> >
<div <div
aria-controls="mx_tabpanel_GENERAL"
aria-selected="true"
class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active" class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active"
data-testid="settings-tab-GENERAL" data-testid="settings-tab-GENERAL"
role="button" role="tab"
tabindex="0" tabindex="0"
> >
<span <span
@ -19,43 +23,52 @@ exports[`<TabbedView /> renders tabs 1`] = `
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_GENERAL_label"
> >
General General
</span> </span>
</div> </div>
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_LABS"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-LABS" data-testid="settings-tab-LABS"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon labs" class="mx_TabbedView_maskedIcon labs"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_LABS_label"
> >
Labs Labs
</span> </span>
</div> </div>
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_SECURITY"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-SECURITY" data-testid="settings-tab-SECURITY"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon security" class="mx_TabbedView_maskedIcon security"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_SECURITY_label"
> >
Security Security
</span> </span>
</div> </div>
</div> </div>
<div <div
aria-labelledby="mx_tabpanel_GENERAL_label"
class="mx_TabbedView_tabPanel" class="mx_TabbedView_tabPanel"
id="mx_tabpanel_GENERAL"
> >
<div <div
class="mx_AutoHideScrollbar mx_TabbedView_tabPanelContent" class="mx_AutoHideScrollbar mx_TabbedView_tabPanelContent"

View file

@ -3,9 +3,11 @@
exports[`<RoomSettingsDialog /> Settings tabs renders default tabs correctly 1`] = ` exports[`<RoomSettingsDialog /> Settings tabs renders default tabs correctly 1`] = `
NodeList [ NodeList [
<div <div
aria-controls="mx_tabpanel_ROOM_GENERAL_TAB"
aria-selected="true"
class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active" class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active"
data-testid="settings-tab-ROOM_GENERAL_TAB" data-testid="settings-tab-ROOM_GENERAL_TAB"
role="button" role="tab"
tabindex="0" tabindex="0"
> >
<span <span
@ -13,66 +15,79 @@ NodeList [
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_ROOM_GENERAL_TAB_label"
> >
General General
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_ROOM_SECURITY_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-ROOM_SECURITY_TAB" data-testid="settings-tab-ROOM_SECURITY_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_securityIcon" class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_securityIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_ROOM_SECURITY_TAB_label"
> >
Security & Privacy Security & Privacy
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_ROOM_ROLES_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-ROOM_ROLES_TAB" data-testid="settings-tab-ROOM_ROLES_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_rolesIcon" class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_rolesIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_ROOM_ROLES_TAB_label"
> >
Roles & Permissions Roles & Permissions
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_ROOM_NOTIFICATIONS_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-ROOM_NOTIFICATIONS_TAB" data-testid="settings-tab-ROOM_NOTIFICATIONS_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_notificationsIcon" class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_notificationsIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_ROOM_NOTIFICATIONS_TAB_label"
> >
Notifications Notifications
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_ROOM_POLL_HISTORY_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-ROOM_POLL_HISTORY_TAB" data-testid="settings-tab-ROOM_POLL_HISTORY_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_pollsIcon" class="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_pollsIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_ROOM_POLL_HISTORY_TAB_label"
> >
Poll history Poll history
</span> </span>

View file

@ -3,9 +3,11 @@
exports[`<UserSettingsDialog /> renders tabs correctly 1`] = ` exports[`<UserSettingsDialog /> renders tabs correctly 1`] = `
NodeList [ NodeList [
<div <div
aria-controls="mx_tabpanel_USER_GENERAL_TAB"
aria-selected="true"
class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active" class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active"
data-testid="settings-tab-USER_GENERAL_TAB" data-testid="settings-tab-USER_GENERAL_TAB"
role="button" role="tab"
tabindex="0" tabindex="0"
> >
<span <span
@ -13,126 +15,151 @@ NodeList [
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_GENERAL_TAB_label"
> >
General General
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_APPEARANCE_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_APPEARANCE_TAB" data-testid="settings-tab-USER_APPEARANCE_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_appearanceIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_appearanceIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_APPEARANCE_TAB_label"
> >
Appearance Appearance
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_NOTIFICATIONS_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_NOTIFICATIONS_TAB" data-testid="settings-tab-USER_NOTIFICATIONS_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_bellIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_bellIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_NOTIFICATIONS_TAB_label"
> >
Notifications Notifications
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_PREFERENCES_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_PREFERENCES_TAB" data-testid="settings-tab-USER_PREFERENCES_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_preferencesIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_preferencesIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_PREFERENCES_TAB_label"
> >
Preferences Preferences
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_KEYBOARD_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_KEYBOARD_TAB" data-testid="settings-tab-USER_KEYBOARD_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_keyboardIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_keyboardIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_KEYBOARD_TAB_label"
> >
Keyboard Keyboard
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_SIDEBAR_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_SIDEBAR_TAB" data-testid="settings-tab-USER_SIDEBAR_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_sidebarIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_sidebarIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_SIDEBAR_TAB_label"
> >
Sidebar Sidebar
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_SECURITY_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_SECURITY_TAB" data-testid="settings-tab-USER_SECURITY_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_securityIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_securityIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_SECURITY_TAB_label"
> >
Security & Privacy Security & Privacy
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_LABS_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_LABS_TAB" data-testid="settings-tab-USER_LABS_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_labsIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_labsIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_LABS_TAB_label"
> >
Labs Labs
</span> </span>
</div>, </div>,
<div <div
class="mx_AccessibleButton mx_TabbedView_tabLabel " aria-controls="mx_tabpanel_USER_HELP_TAB"
aria-selected="false"
class="mx_AccessibleButton mx_TabbedView_tabLabel"
data-testid="settings-tab-USER_HELP_TAB" data-testid="settings-tab-USER_HELP_TAB"
role="button" role="tab"
tabindex="0" tabindex="-1"
> >
<span <span
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_helpIcon" class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_helpIcon"
/> />
<span <span
class="mx_TabbedView_tabLabel_text" class="mx_TabbedView_tabLabel_text"
id="mx_tabpanel_USER_HELP_TAB_label"
> >
Help & About Help & About
</span> </span>