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

Commit

Permalink
Iterate landmarks around the app in order to improve a11y (#12064)
Browse files Browse the repository at this point in the history
* Iterate landmarks around the app in order to improve a11y

Signed-off-by: Michael Telatynski <[email protected]>

* Add missing aria-label

Signed-off-by: Michael Telatynski <[email protected]>

* Update snapshots

Signed-off-by: Michael Telatynski <[email protected]>

* i18n

Signed-off-by: Michael Telatynski <[email protected]>

* Fix tests

Signed-off-by: Michael Telatynski <[email protected]>

* Iterate

Signed-off-by: Michael Telatynski <[email protected]>

* Update screenshots which have changed a fraction due to default heading margins being different in different landmarks

Signed-off-by: Michael Telatynski <[email protected]>

---------

Signed-off-by: Michael Telatynski <[email protected]>
  • Loading branch information
t3chguy authored Dec 20, 2023
1 parent af31965 commit 0a881e2
Show file tree
Hide file tree
Showing 25 changed files with 68 additions and 40 deletions.
2 changes: 1 addition & 1 deletion playwright/e2e/user-view/user-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test.describe("UserView", () => {
test("should render the user view as expected", async ({ page, homeserver, user, bot }) => {
await page.goto(`/#/user/${bot.credentials.userId}`);

const rightPanel = page.getByRole("complementary");
const rightPanel = page.locator("#mx_RightPanel");
await expect(rightPanel.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
await expect(rightPanel.getByText("1 session")).toBeVisible();
await expect(rightPanel).toMatchScreenshot("user-info.png", {
Expand Down
4 changes: 2 additions & 2 deletions playwright/pages/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ export class Settings {
}

/**
* Open room settings (via room menu), returns a locator to the dialog
* Open room settings (via room header menu), returns a locator to the dialog
* @param tab the name of the tab to switch to after opening, optional.
*/
public async openRoomSettings(tab?: string): Promise<Locator> {
await this.page.getByRole("main").getByRole("button", { name: "Room options", exact: true }).click();
await this.page.getByRole("banner").getByRole("button", { name: "Room options", exact: true }).click();
await this.page.locator(".mx_RoomTile_contextMenu").getByRole("menuitem", { name: "Settings" }).click();
if (tab) await this.switchTab(tab);
return this.page.locator(".mx_Dialog").filter({ has: this.page.locator(".mx_RoomSettingsDialog") });
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions src/components/structures/LeftPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
if (this.state.showBreadcrumbs === BreadcrumbsMode.Legacy && !this.props.isMinimized) {
return (
<IndicatorScrollbar
role="navigation"
aria-label={_t("a11y|recent_rooms")}
className="mx_LeftPanel_breadcrumbsContainer mx_AutoHideScrollbar"
verticalScrollsHorizontally={true}
>
Expand Down Expand Up @@ -356,6 +358,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
role="search"
>
<RoomSearch isMinimized={this.props.isMinimized} />

Expand Down Expand Up @@ -397,7 +400,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
selected={this.props.pageType === PageType.HomePage}
minimized={this.props.isMinimized}
/>
<div className="mx_LeftPanel_roomListWrapper">
<nav className="mx_LeftPanel_roomListWrapper" aria-label={_t("common|rooms")}>
<div
className={roomListClasses}
ref={this.listContainerRef}
Expand All @@ -407,7 +410,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
>
{roomList}
</div>
</div>
</nav>
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ class LoggedInView extends React.Component<IProps, IState> {
<div className={bodyClasses}>
<div className="mx_LeftPanel_outerWrapper">
<LeftPanelLiveShareWarning isMinimized={this.props.collapseLhs || false} />
<nav className="mx_LeftPanel_wrapper">
<div className="mx_LeftPanel_wrapper">
<BackdropPanel blurMultiplier={0.5} backgroundImage={this.state.backgroundImage} />
<SpacePanel />
<BackdropPanel backgroundImage={this.state.backgroundImage} />
Expand All @@ -698,7 +698,7 @@ class LoggedInView extends React.Component<IProps, IState> {
resizeNotifier={this.props.resizeNotifier}
/>
</div>
</nav>
</div>
</div>
<ResizeHandle passRef={this.resizeHandler} id="lp-resizer" />
<div className="mx_RoomView_wrapper">{pageElement}</div>
Expand Down
14 changes: 8 additions & 6 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private unmounted = false;
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};

private roomView = createRef<HTMLElement>();
private roomView = createRef<HTMLDivElement>();
private searchResultsPanel = createRef<ScrollPanel>();
private messagePanel: TimelinePanel | null = null;
private roomViewBody = createRef<HTMLDivElement>();
Expand Down Expand Up @@ -2302,7 +2302,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// if statusBar does not exist then statusBarArea is blank and takes up unnecessary space on the screen
// show statusBarArea only if statusBar is present
const statusBarArea = statusBar && (
<div className={statusBarAreaClass}>
<div role="region" className={statusBarAreaClass} aria-label={_t("a11y|room_status_bar")}>
<div className="mx_RoomView_statusAreaBox">
<div className="mx_RoomView_statusAreaBox_line" />
{statusBar}
Expand Down Expand Up @@ -2528,13 +2528,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
<Measured sensor={this.roomViewBody.current} onMeasurement={this.onMeasurement} />
)}
{auxPanel}
<div className={timelineClasses}>
<main className={timelineClasses}>
<FileDropTarget parent={this.roomView.current} onFileDrop={this.onFileDrop} />
{topUnreadMessagesBar}
{jumpToBottom}
{messagePanel}
{searchResultsPanel}
</div>
</main>
{statusBarArea}
{previewBar}
{messageComposer}
Expand All @@ -2550,6 +2550,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
userId={this.context.client.getSafeUserId()}
resizeNotifier={this.props.resizeNotifier}
showApps={true}
role="main"
/>
{previewBar}
</>
Expand All @@ -2563,6 +2564,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
room={this.state.room}
resizing={this.state.resizing}
waitForCall={isVideoRoom(this.state.room)}
role="main"
/>
{previewBar}
</>
Expand Down Expand Up @@ -2603,7 +2605,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {

return (
<RoomContext.Provider value={this.state}>
<main
<div
className={mainClasses}
ref={this.roomView}
onKeyDown={this.onReactKeyDown}
Expand Down Expand Up @@ -2655,7 +2657,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
</div>
</MainSplit>
</ErrorBoundary>
</main>
</div>
</RoomContext.Provider>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/ThreadPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,11 @@ const EmptyThread: React.FC<EmptyThreadIProps> = ({ hasThreads, filterOption, sh
}

return (
<aside className="mx_ThreadPanel_empty">
<div className="mx_ThreadPanel_empty">
<div className="mx_ThreadPanel_largeIcon" />
<h2>{_t("threads|empty_heading")}</h2>
{body}
</aside>
</div>
);
};

Expand Down
9 changes: 7 additions & 2 deletions src/components/views/emojipicker/EmojiPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ class EmojiPicker extends React.Component<IProps, IState> {
{({ onKeyDownHandler }) => {
let heightBefore = 0;
return (
<div className="mx_EmojiPicker" data-testid="mx_EmojiPicker" onKeyDown={onKeyDownHandler}>
<section
className="mx_EmojiPicker"
data-testid="mx_EmojiPicker"
onKeyDown={onKeyDownHandler}
aria-label={_t("a11y|emoji_picker")}
>
<Header categories={this.categories} onAnchorClick={this.scrollToCategory} />
<Search
query={this.state.filter}
Expand Down Expand Up @@ -407,7 +412,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
selectedEmojis={this.props.selectedEmojis}
/>
)}
</div>
</section>
);
}}
</RovingTabIndexProvider>
Expand Down
5 changes: 3 additions & 2 deletions src/components/views/rooms/AppsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import React, { AriaRole } from "react";
import classNames from "classnames";
import { Resizable, Size } from "re-resizable";
import { Room } from "matrix-js-sdk/src/matrix";
Expand All @@ -42,6 +42,7 @@ interface IProps {
resizeNotifier: ResizeNotifier;
showApps?: boolean; // Should apps be rendered
maxHeight: number;
role?: AriaRole;
}

interface IState {
Expand Down Expand Up @@ -294,7 +295,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
}

return (
<div className={classes}>
<div role={this.props.role} className={classes}>
{drawer}
{spinner}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/AuxPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class AuxPanel extends React.Component<IProps> {
}

return (
<AutoHideScrollbar className="mx_AuxPanel">
<AutoHideScrollbar role="region" className="mx_AuxPanel">
{this.props.children}
{appsDrawer}
{callView}
Expand Down
2 changes: 2 additions & 0 deletions src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ export class MessageComposer extends React.Component<IProps, IState> {
className={classes}
ref={this.ref}
aria-describedby={this.state.recordingTimeLeftSeconds ? this.tooltipId : undefined}
role="region"
aria-label={_t("a11y|message_composer")}
>
{recordingTooltip}
<div className="mx_MessageComposer_wrapper">
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/RoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export default function RoomHeader({
)}
</Box>
</button>
<Flex as="nav" align="center" gap="var(--cpd-space-2x)">
<Flex align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map((props) => {
const label = props.label();

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/rooms/RoomListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
}

return (
<div className="mx_RoomListHeader">
<aside className="mx_RoomListHeader" aria-label={_t("room|context_menu|title")}>
{contextMenuButton}
{pendingActionSummary ? (
<TooltipTarget label={pendingActionSummary}>
Expand All @@ -427,7 +427,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
)}

{contextMenu}
</div>
</aside>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/RoomPreviewBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
);

return (
<div className={classes}>
<div role="complementary" className={classes}>
<div className="mx_RoomPreviewBar_message">
{titleElement}
{subTitleElements}
Expand Down
5 changes: 3 additions & 2 deletions src/components/views/spaces/SpacePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,11 @@ const SpacePanel: React.FC = () => {
onDragEndHandler();
}}
>
<div
<nav
className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
onKeyDown={onKeyDownHandler}
ref={ref}
aria-label={_t("common|spaces")}
>
<UserMenu isPanelCollapsed={isPanelCollapsed}>
<AccessibleTooltipButton
Expand Down Expand Up @@ -406,7 +407,7 @@ const SpacePanel: React.FC = () => {
</Droppable>

<QuickSettingsButton isPanelCollapsed={isPanelCollapsed} />
</div>
</nav>
</DragDropContext>
)}
</RovingTabIndexProvider>
Expand Down
21 changes: 13 additions & 8 deletions src/components/views/voip/CallView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { FC, ReactNode, useState, useContext, useEffect, useMemo, useRef, useCallback } from "react";
import React, { FC, ReactNode, useState, useContext, useEffect, useMemo, useRef, useCallback, AriaRole } from "react";
import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
Expand Down Expand Up @@ -297,9 +297,10 @@ interface StartCallViewProps {
resizing: boolean;
call: Call | null;
setStartingCall: (value: boolean) => void;
role?: AriaRole;
}

const StartCallView: FC<StartCallViewProps> = ({ room, resizing, call, setStartingCall }) => {
const StartCallView: FC<StartCallViewProps> = ({ room, resizing, call, setStartingCall, role }) => {
const cli = useContext(MatrixClientContext);

// Since connection has to be split across two different callbacks, we
Expand Down Expand Up @@ -348,7 +349,7 @@ const StartCallView: FC<StartCallViewProps> = ({ room, resizing, call, setStarti
}, [call, connectDeferred]);

return (
<div className="mx_CallView">
<div className="mx_CallView" role={role}>
{connected ? null : <Lobby room={room} connect={connect} />}
{call !== null && (
<AppTile
Expand All @@ -369,9 +370,10 @@ interface JoinCallViewProps {
room: Room;
resizing: boolean;
call: Call;
role?: AriaRole;
}

const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => {
const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call, role }) => {
const cli = useContext(MatrixClientContext);
const connected = isConnected(useConnectionState(call));
const members = useParticipatingMembers(call);
Expand Down Expand Up @@ -415,7 +417,7 @@ const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => {
}

return (
<div className="mx_CallView">
<div className="mx_CallView" role={role}>
{lobby}
{/* We render the widget even if we're disconnected, so it stays loaded */}
<AppTile
Expand All @@ -439,16 +441,19 @@ interface CallViewProps {
* button will create a call if there isn't already one.
*/
waitForCall: boolean;
role?: AriaRole;
}

export const CallView: FC<CallViewProps> = ({ room, resizing, waitForCall }) => {
export const CallView: FC<CallViewProps> = ({ room, resizing, waitForCall, role }) => {
const call = useCall(room.roomId);
const [startingCall, setStartingCall] = useState(false);

if (call === null || startingCall) {
if (waitForCall) return null;
return <StartCallView room={room} resizing={resizing} call={call} setStartingCall={setStartingCall} />;
return (
<StartCallView room={room} resizing={resizing} call={call} setStartingCall={setStartingCall} role={role} />
);
} else {
return <JoinCallView room={room} resizing={resizing} call={call} />;
return <JoinCallView room={room} resizing={resizing} call={call} role={role} />;
}
};
4 changes: 4 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"a11y": {
"emoji_picker": "Emoji picker",
"jump_first_invite": "Jump to first invite.",
"message_composer": "Message composer",
"n_unread_messages": {
"one": "1 unread message.",
"other": "%(count)s unread messages."
Expand All @@ -9,7 +11,9 @@
"one": "1 unread mention.",
"other": "%(count)s unread messages including mentions."
},
"recent_rooms": "Recent rooms",
"room_name": "Room %(name)s",
"room_status_bar": "Room status bar",
"unread_messages": "Unread messages.",
"user_menu": "User menu"
},
Expand Down
4 changes: 2 additions & 2 deletions src/utils/exportUtils/HtmlExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export default class HTMLExporter extends Exporter {
<title>${_t("export_chat|html_title")}</title>
</head>
<body style="height: 100vh;">
<section
<div
id="matrixchat"
style="height: 100%; overflow: auto"
class="notranslate"
Expand Down Expand Up @@ -237,7 +237,7 @@ export default class HTMLExporter extends Exporter {
</main>
</div>
</div>
</section>
</div>
<div id="snackbar"/>
</body>
</html>`;
Expand Down
Loading

0 comments on commit 0a881e2

Please sign in to comment.