Iterate landmarks around the app in order to improve a11y (#12064)

* Iterate landmarks around the app in order to improve a11y

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add missing aria-label

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* i18n

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

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

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2023-12-20 15:32:24 +00:00 committed by GitHub
parent af31965866
commit 0a881e2123
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 68 additions and 40 deletions

View file

@ -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}
>
@ -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} />
@ -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}
@ -407,7 +410,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
>
{roomList}
</div>
</div>
</nav>
</div>
</div>
);

View file

@ -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} />
@ -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>

View file

@ -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>();
@ -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}
@ -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}
@ -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}
</>
@ -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}
</>
@ -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}
@ -2655,7 +2657,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
</div>
</MainSplit>
</ErrorBoundary>
</main>
</div>
</RoomContext.Provider>
);
}

View file

@ -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>
);
};

View file

@ -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}
@ -407,7 +412,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
selectedEmojis={this.props.selectedEmojis}
/>
)}
</div>
</section>
);
}}
</RovingTabIndexProvider>

View file

@ -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";
@ -42,6 +42,7 @@ interface IProps {
resizeNotifier: ResizeNotifier;
showApps?: boolean; // Should apps be rendered
maxHeight: number;
role?: AriaRole;
}
interface IState {
@ -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>

View file

@ -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}

View file

@ -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">

View file

@ -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();

View file

@ -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}>
@ -427,7 +427,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
)}
{contextMenu}
</div>
</aside>
);
};

View file

@ -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}

View file

@ -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
@ -406,7 +407,7 @@ const SpacePanel: React.FC = () => {
</Droppable>
<QuickSettingsButton isPanelCollapsed={isPanelCollapsed} />
</div>
</nav>
</DragDropContext>
)}
</RovingTabIndexProvider>

View file

@ -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";
@ -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
@ -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
@ -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);
@ -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
@ -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} />;
}
};

View file

@ -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."
@ -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"
},

View file

@ -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"
@ -237,7 +237,7 @@ export default class HTMLExporter extends Exporter {
</main>
</div>
</div>
</section>
</div>
<div id="snackbar"/>
</body>
</html>`;