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>
pull/28788/head^2
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

@ -25,7 +25,7 @@ test.describe("UserView", () => {
test("should render the user view as expected", async ({ page, homeserver, user, bot }) => { test("should render the user view as expected", async ({ page, homeserver, user, bot }) => {
await page.goto(`/#/user/${bot.credentials.userId}`); 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.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
await expect(rightPanel.getByText("1 session")).toBeVisible(); await expect(rightPanel.getByText("1 session")).toBeVisible();
await expect(rightPanel).toMatchScreenshot("user-info.png", { await expect(rightPanel).toMatchScreenshot("user-info.png", {

View File

@ -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. * @param tab the name of the tab to switch to after opening, optional.
*/ */
public async openRoomSettings(tab?: string): Promise<Locator> { 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(); await this.page.locator(".mx_RoomTile_contextMenu").getByRole("menuitem", { name: "Settings" }).click();
if (tab) await this.switchTab(tab); if (tab) await this.switchTab(tab);
return this.page.locator(".mx_Dialog").filter({ has: this.page.locator(".mx_RoomSettingsDialog") }); return this.page.locator(".mx_Dialog").filter({ has: this.page.locator(".mx_RoomSettingsDialog") });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 KiB

After

Width:  |  Height:  |  Size: 563 KiB

View File

@ -315,6 +315,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
if (this.state.showBreadcrumbs === BreadcrumbsMode.Legacy && !this.props.isMinimized) { if (this.state.showBreadcrumbs === BreadcrumbsMode.Legacy && !this.props.isMinimized) {
return ( return (
<IndicatorScrollbar <IndicatorScrollbar
role="navigation"
aria-label={_t("a11y|recent_rooms")}
className="mx_LeftPanel_breadcrumbsContainer mx_AutoHideScrollbar" className="mx_LeftPanel_breadcrumbsContainer mx_AutoHideScrollbar"
verticalScrollsHorizontally={true} verticalScrollsHorizontally={true}
> >
@ -356,6 +358,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
onFocus={this.onFocus} onFocus={this.onFocus}
onBlur={this.onBlur} onBlur={this.onBlur}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
role="search"
> >
<RoomSearch isMinimized={this.props.isMinimized} /> <RoomSearch isMinimized={this.props.isMinimized} />
@ -397,7 +400,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
selected={this.props.pageType === PageType.HomePage} selected={this.props.pageType === PageType.HomePage}
minimized={this.props.isMinimized} minimized={this.props.isMinimized}
/> />
<div className="mx_LeftPanel_roomListWrapper"> <nav className="mx_LeftPanel_roomListWrapper" aria-label={_t("common|rooms")}>
<div <div
className={roomListClasses} className={roomListClasses}
ref={this.listContainerRef} ref={this.listContainerRef}
@ -407,7 +410,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
> >
{roomList} {roomList}
</div> </div>
</div> </nav>
</div> </div>
</div> </div>
); );

View File

@ -683,7 +683,7 @@ class LoggedInView extends React.Component<IProps, IState> {
<div className={bodyClasses}> <div className={bodyClasses}>
<div className="mx_LeftPanel_outerWrapper"> <div className="mx_LeftPanel_outerWrapper">
<LeftPanelLiveShareWarning isMinimized={this.props.collapseLhs || false} /> <LeftPanelLiveShareWarning isMinimized={this.props.collapseLhs || false} />
<nav className="mx_LeftPanel_wrapper"> <div className="mx_LeftPanel_wrapper">
<BackdropPanel blurMultiplier={0.5} backgroundImage={this.state.backgroundImage} /> <BackdropPanel blurMultiplier={0.5} backgroundImage={this.state.backgroundImage} />
<SpacePanel /> <SpacePanel />
<BackdropPanel backgroundImage={this.state.backgroundImage} /> <BackdropPanel backgroundImage={this.state.backgroundImage} />
@ -698,7 +698,7 @@ class LoggedInView extends React.Component<IProps, IState> {
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
/> />
</div> </div>
</nav> </div>
</div> </div>
<ResizeHandle passRef={this.resizeHandler} id="lp-resizer" /> <ResizeHandle passRef={this.resizeHandler} id="lp-resizer" />
<div className="mx_RoomView_wrapper">{pageElement}</div> <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 unmounted = false;
private permalinkCreators: Record<string, RoomPermalinkCreator> = {}; private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
private roomView = createRef<HTMLElement>(); private roomView = createRef<HTMLDivElement>();
private searchResultsPanel = createRef<ScrollPanel>(); private searchResultsPanel = createRef<ScrollPanel>();
private messagePanel: TimelinePanel | null = null; private messagePanel: TimelinePanel | null = null;
private roomViewBody = createRef<HTMLDivElement>(); 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 // if statusBar does not exist then statusBarArea is blank and takes up unnecessary space on the screen
// show statusBarArea only if statusBar is present // show statusBarArea only if statusBar is present
const statusBarArea = statusBar && ( 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">
<div className="mx_RoomView_statusAreaBox_line" /> <div className="mx_RoomView_statusAreaBox_line" />
{statusBar} {statusBar}
@ -2528,13 +2528,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
<Measured sensor={this.roomViewBody.current} onMeasurement={this.onMeasurement} /> <Measured sensor={this.roomViewBody.current} onMeasurement={this.onMeasurement} />
)} )}
{auxPanel} {auxPanel}
<div className={timelineClasses}> <main className={timelineClasses}>
<FileDropTarget parent={this.roomView.current} onFileDrop={this.onFileDrop} /> <FileDropTarget parent={this.roomView.current} onFileDrop={this.onFileDrop} />
{topUnreadMessagesBar} {topUnreadMessagesBar}
{jumpToBottom} {jumpToBottom}
{messagePanel} {messagePanel}
{searchResultsPanel} {searchResultsPanel}
</div> </main>
{statusBarArea} {statusBarArea}
{previewBar} {previewBar}
{messageComposer} {messageComposer}
@ -2550,6 +2550,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
userId={this.context.client.getSafeUserId()} userId={this.context.client.getSafeUserId()}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
showApps={true} showApps={true}
role="main"
/> />
{previewBar} {previewBar}
</> </>
@ -2563,6 +2564,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
room={this.state.room} room={this.state.room}
resizing={this.state.resizing} resizing={this.state.resizing}
waitForCall={isVideoRoom(this.state.room)} waitForCall={isVideoRoom(this.state.room)}
role="main"
/> />
{previewBar} {previewBar}
</> </>
@ -2603,7 +2605,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
return ( return (
<RoomContext.Provider value={this.state}> <RoomContext.Provider value={this.state}>
<main <div
className={mainClasses} className={mainClasses}
ref={this.roomView} ref={this.roomView}
onKeyDown={this.onReactKeyDown} onKeyDown={this.onReactKeyDown}
@ -2655,7 +2657,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
</div> </div>
</MainSplit> </MainSplit>
</ErrorBoundary> </ErrorBoundary>
</main> </div>
</RoomContext.Provider> </RoomContext.Provider>
); );
} }

View File

@ -180,11 +180,11 @@ const EmptyThread: React.FC<EmptyThreadIProps> = ({ hasThreads, filterOption, sh
} }
return ( return (
<aside className="mx_ThreadPanel_empty"> <div className="mx_ThreadPanel_empty">
<div className="mx_ThreadPanel_largeIcon" /> <div className="mx_ThreadPanel_largeIcon" />
<h2>{_t("threads|empty_heading")}</h2> <h2>{_t("threads|empty_heading")}</h2>
{body} {body}
</aside> </div>
); );
}; };

View File

@ -362,7 +362,12 @@ class EmojiPicker extends React.Component<IProps, IState> {
{({ onKeyDownHandler }) => { {({ onKeyDownHandler }) => {
let heightBefore = 0; let heightBefore = 0;
return ( 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} /> <Header categories={this.categories} onAnchorClick={this.scrollToCategory} />
<Search <Search
query={this.state.filter} query={this.state.filter}
@ -407,7 +412,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
selectedEmojis={this.props.selectedEmojis} selectedEmojis={this.props.selectedEmojis}
/> />
)} )}
</div> </section>
); );
}} }}
</RovingTabIndexProvider> </RovingTabIndexProvider>

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from "react"; import React, { AriaRole } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { Resizable, Size } from "re-resizable"; import { Resizable, Size } from "re-resizable";
import { Room } from "matrix-js-sdk/src/matrix"; import { Room } from "matrix-js-sdk/src/matrix";
@ -42,6 +42,7 @@ interface IProps {
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
showApps?: boolean; // Should apps be rendered showApps?: boolean; // Should apps be rendered
maxHeight: number; maxHeight: number;
role?: AriaRole;
} }
interface IState { interface IState {
@ -294,7 +295,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
} }
return ( return (
<div className={classes}> <div role={this.props.role} className={classes}>
{drawer} {drawer}
{spinner} {spinner}
</div> </div>

View File

@ -65,7 +65,7 @@ export default class AuxPanel extends React.Component<IProps> {
} }
return ( return (
<AutoHideScrollbar className="mx_AuxPanel"> <AutoHideScrollbar role="region" className="mx_AuxPanel">
{this.props.children} {this.props.children}
{appsDrawer} {appsDrawer}
{callView} {callView}

View File

@ -602,6 +602,8 @@ export class MessageComposer extends React.Component<IProps, IState> {
className={classes} className={classes}
ref={this.ref} ref={this.ref}
aria-describedby={this.state.recordingTimeLeftSeconds ? this.tooltipId : undefined} aria-describedby={this.state.recordingTimeLeftSeconds ? this.tooltipId : undefined}
role="region"
aria-label={_t("a11y|message_composer")}
> >
{recordingTooltip} {recordingTooltip}
<div className="mx_MessageComposer_wrapper"> <div className="mx_MessageComposer_wrapper">

View File

@ -182,7 +182,7 @@ export default function RoomHeader({
)} )}
</Box> </Box>
</button> </button>
<Flex as="nav" align="center" gap="var(--cpd-space-2x)"> <Flex align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map((props) => { {additionalButtons?.map((props) => {
const label = props.label(); const label = props.label();

View File

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

View File

@ -720,7 +720,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
); );
return ( return (
<div className={classes}> <div role="complementary" className={classes}>
<div className="mx_RoomPreviewBar_message"> <div className="mx_RoomPreviewBar_message">
{titleElement} {titleElement}
{subTitleElements} {subTitleElements}

View File

@ -364,10 +364,11 @@ const SpacePanel: React.FC = () => {
onDragEndHandler(); onDragEndHandler();
}} }}
> >
<div <nav
className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })} className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
onKeyDown={onKeyDownHandler} onKeyDown={onKeyDownHandler}
ref={ref} ref={ref}
aria-label={_t("common|spaces")}
> >
<UserMenu isPanelCollapsed={isPanelCollapsed}> <UserMenu isPanelCollapsed={isPanelCollapsed}>
<AccessibleTooltipButton <AccessibleTooltipButton
@ -406,7 +407,7 @@ const SpacePanel: React.FC = () => {
</Droppable> </Droppable>
<QuickSettingsButton isPanelCollapsed={isPanelCollapsed} /> <QuickSettingsButton isPanelCollapsed={isPanelCollapsed} />
</div> </nav>
</DragDropContext> </DragDropContext>
)} )}
</RovingTabIndexProvider> </RovingTabIndexProvider>

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. 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 classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { defer, IDeferred } from "matrix-js-sdk/src/utils"; import { defer, IDeferred } from "matrix-js-sdk/src/utils";
@ -297,9 +297,10 @@ interface StartCallViewProps {
resizing: boolean; resizing: boolean;
call: Call | null; call: Call | null;
setStartingCall: (value: boolean) => void; 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); const cli = useContext(MatrixClientContext);
// Since connection has to be split across two different callbacks, we // 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]); }, [call, connectDeferred]);
return ( return (
<div className="mx_CallView"> <div className="mx_CallView" role={role}>
{connected ? null : <Lobby room={room} connect={connect} />} {connected ? null : <Lobby room={room} connect={connect} />}
{call !== null && ( {call !== null && (
<AppTile <AppTile
@ -369,9 +370,10 @@ interface JoinCallViewProps {
room: Room; room: Room;
resizing: boolean; resizing: boolean;
call: Call; call: Call;
role?: AriaRole;
} }
const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => { const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call, role }) => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const connected = isConnected(useConnectionState(call)); const connected = isConnected(useConnectionState(call));
const members = useParticipatingMembers(call); const members = useParticipatingMembers(call);
@ -415,7 +417,7 @@ const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => {
} }
return ( return (
<div className="mx_CallView"> <div className="mx_CallView" role={role}>
{lobby} {lobby}
{/* We render the widget even if we're disconnected, so it stays loaded */} {/* We render the widget even if we're disconnected, so it stays loaded */}
<AppTile <AppTile
@ -439,16 +441,19 @@ interface CallViewProps {
* button will create a call if there isn't already one. * button will create a call if there isn't already one.
*/ */
waitForCall: boolean; 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 call = useCall(room.roomId);
const [startingCall, setStartingCall] = useState(false); const [startingCall, setStartingCall] = useState(false);
if (call === null || startingCall) { if (call === null || startingCall) {
if (waitForCall) return null; 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 { } 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": { "a11y": {
"emoji_picker": "Emoji picker",
"jump_first_invite": "Jump to first invite.", "jump_first_invite": "Jump to first invite.",
"message_composer": "Message composer",
"n_unread_messages": { "n_unread_messages": {
"one": "1 unread message.", "one": "1 unread message.",
"other": "%(count)s unread messages." "other": "%(count)s unread messages."
@ -9,7 +11,9 @@
"one": "1 unread mention.", "one": "1 unread mention.",
"other": "%(count)s unread messages including mentions." "other": "%(count)s unread messages including mentions."
}, },
"recent_rooms": "Recent rooms",
"room_name": "Room %(name)s", "room_name": "Room %(name)s",
"room_status_bar": "Room status bar",
"unread_messages": "Unread messages.", "unread_messages": "Unread messages.",
"user_menu": "User menu" "user_menu": "User menu"
}, },

View File

@ -164,7 +164,7 @@ export default class HTMLExporter extends Exporter {
<title>${_t("export_chat|html_title")}</title> <title>${_t("export_chat|html_title")}</title>
</head> </head>
<body style="height: 100vh;"> <body style="height: 100vh;">
<section <div
id="matrixchat" id="matrixchat"
style="height: 100%; overflow: auto" style="height: 100%; overflow: auto"
class="notranslate" class="notranslate"
@ -237,7 +237,7 @@ export default class HTMLExporter extends Exporter {
</main> </main>
</div> </div>
</div> </div>
</section> </div>
<div id="snackbar"/> <div id="snackbar"/>
</body> </body>
</html>`; </html>`;

View File

@ -373,7 +373,9 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</div> </div>
</div> </div>
<div <div
aria-label="Message composer"
class="mx_MessageComposer" class="mx_MessageComposer"
role="region"
> >
<div <div
class="mx_MessageComposer_wrapper" class="mx_MessageComposer_wrapper"
@ -614,7 +616,9 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</div> </div>
</div> </div>
<div <div
aria-label="Message composer"
class="mx_MessageComposer" class="mx_MessageComposer"
role="region"
> >
<div <div
class="mx_MessageComposer_wrapper" class="mx_MessageComposer_wrapper"
@ -740,6 +744,7 @@ exports[`RoomView should show error view if failed to look up room alias 1`] = `
> >
<div <div
class="mx_RoomPreviewBar mx_RoomPreviewBar_RoomNotFound mx_RoomPreviewBar_dialog" class="mx_RoomPreviewBar mx_RoomPreviewBar_RoomNotFound mx_RoomPreviewBar_dialog"
role="complementary"
> >
<div <div
class="mx_RoomPreviewBar_message" class="mx_RoomPreviewBar_message"

View File

@ -39,7 +39,7 @@ exports[`RoomHeader does not show the face pile for DMs 1`] = `
</div> </div>
</div> </div>
</button> </button>
<nav <div
class="mx_Flex" class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);" style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
> >
@ -75,7 +75,7 @@ exports[`RoomHeader does not show the face pile for DMs 1`] = `
> >
<div /> <div />
</button> </button>
</nav> </div>
</header> </header>
</DocumentFragment> </DocumentFragment>
`; `;

View File

@ -13,7 +13,7 @@ exports[`HTMLExport should export 1`] = `
<title>Exported Data</title> <title>Exported Data</title>
</head> </head>
<body style="height: 100vh;"> <body style="height: 100vh;">
<section <div
id="matrixchat" id="matrixchat"
style="height: 100%; overflow: auto" style="height: 100%; overflow: auto"
class="notranslate" class="notranslate"
@ -82,7 +82,7 @@ exports[`HTMLExport should export 1`] = `
</main> </main>
</div> </div>
</div> </div>
</section> </div>
<div id="snackbar"/> <div id="snackbar"/>
</body> </body>
</html>" </html>"