New layout selector ui in user settings (#12676)
* feat: reworked the layout switcher * feat: make the classname optional in EventTilePreview.tsx * test: add tests to LayoutSwitcher * feat: change appearance tab * test: update appearance snapshot * e2e: add tests * css: add comment for gap overridingpull/28217/head
parent
6f5d21fedb
commit
2f953f1d0f
|
@ -33,43 +33,6 @@ test.describe("Appearance user settings tab", () => {
|
|||
await expect(tab).toMatchScreenshot("appearance-tab.png");
|
||||
});
|
||||
|
||||
test("should support switching layouts", async ({ page, user, app }) => {
|
||||
// Create and view a room first
|
||||
await app.client.createRoom({ name: "Test Room" });
|
||||
await app.viewRoomByName("Test Room");
|
||||
|
||||
await app.settings.openUserSettings("Appearance");
|
||||
|
||||
const buttons = page.locator(".mx_LayoutSwitcher_RadioButton");
|
||||
|
||||
// Assert that the layout selected by default is "Modern"
|
||||
await expect(
|
||||
buttons.locator(".mx_StyledRadioButton_enabled", {
|
||||
hasText: "Modern",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// Assert that the room layout is set to group (modern) layout
|
||||
await expect(page.locator(".mx_RoomView_body[data-layout='group']")).toBeVisible();
|
||||
|
||||
// Select the first layout
|
||||
await buttons.first().click();
|
||||
// Assert that the layout selected is "IRC (Experimental)"
|
||||
await expect(buttons.locator(".mx_StyledRadioButton_enabled", { hasText: "IRC (Experimental)" })).toBeVisible();
|
||||
|
||||
// Assert that the room layout is set to IRC layout
|
||||
await expect(page.locator(".mx_RoomView_body[data-layout='irc']")).toBeVisible();
|
||||
|
||||
// Select the last layout
|
||||
await buttons.last().click();
|
||||
|
||||
// Assert that the layout selected is "Message bubbles"
|
||||
await expect(buttons.locator(".mx_StyledRadioButton_enabled", { hasText: "Message bubbles" })).toBeVisible();
|
||||
|
||||
// Assert that the room layout is set to bubble layout
|
||||
await expect(page.locator(".mx_RoomView_body[data-layout='bubble']")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should support changing font size by using the font size dropdown", async ({ page, app, user }) => {
|
||||
await app.settings.openUserSettings("Appearance");
|
||||
|
||||
|
@ -84,57 +47,6 @@ test.describe("Appearance user settings tab", () => {
|
|||
await expect(page).toMatchScreenshot("window-12px.png");
|
||||
});
|
||||
|
||||
test("should support enabling compact group (modern) layout", async ({ page, app, user }) => {
|
||||
// Create and view a room first
|
||||
await app.client.createRoom({ name: "Test Room" });
|
||||
await app.viewRoomByName("Test Room");
|
||||
|
||||
await app.settings.openUserSettings("Appearance");
|
||||
|
||||
// Click "Show advanced" link button
|
||||
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
|
||||
await tab.getByRole("button", { name: "Show advanced" }).click();
|
||||
|
||||
await tab.locator("label", { hasText: "Use a more compact 'Modern' layout" }).click();
|
||||
|
||||
// Assert that the room layout is set to compact group (modern) layout
|
||||
await expect(page.locator("#matrixchat .mx_MatrixChat_wrapper.mx_MatrixChat_useCompactLayout")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should disable compact group (modern) layout option on IRC layout and bubble layout", async ({
|
||||
page,
|
||||
app,
|
||||
user,
|
||||
}) => {
|
||||
await app.settings.openUserSettings("Appearance");
|
||||
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
|
||||
|
||||
const checkDisabled = async () => {
|
||||
await expect(tab.getByRole("checkbox", { name: "Use a more compact 'Modern' layout" })).toBeDisabled();
|
||||
};
|
||||
|
||||
// Click "Show advanced" link button
|
||||
await tab.getByRole("button", { name: "Show advanced" }).click();
|
||||
|
||||
const buttons = page.locator(".mx_LayoutSwitcher_RadioButton");
|
||||
|
||||
// Enable IRC layout
|
||||
await buttons.first().click();
|
||||
|
||||
// Assert that the layout selected is "IRC (Experimental)"
|
||||
await expect(buttons.locator(".mx_StyledRadioButton_enabled", { hasText: "IRC (Experimental)" })).toBeVisible();
|
||||
|
||||
await checkDisabled();
|
||||
|
||||
// Enable bubble layout
|
||||
await buttons.last().click();
|
||||
|
||||
// Assert that the layout selected is "IRC (Experimental)"
|
||||
await expect(buttons.locator(".mx_StyledRadioButton_enabled", { hasText: "Message bubbles" })).toBeVisible();
|
||||
|
||||
await checkDisabled();
|
||||
});
|
||||
|
||||
test("should support enabling system font", async ({ page, app, user }) => {
|
||||
await app.settings.openUserSettings("Appearance");
|
||||
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
|
||||
|
@ -149,6 +61,49 @@ test.describe("Appearance user settings tab", () => {
|
|||
await expect(page.locator("body")).toHaveCSS("font-family", '""');
|
||||
});
|
||||
|
||||
test.describe("Message Layout Panel", () => {
|
||||
test.beforeEach(async ({ app, user, util }) => {
|
||||
await util.createAndDisplayRoom();
|
||||
await util.assertModernLayout();
|
||||
await util.openAppearanceTab();
|
||||
});
|
||||
|
||||
test("should change the message layout from modern to bubble", async ({ page, app, user, util }) => {
|
||||
await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-modern.png");
|
||||
|
||||
await util.getBubbleLayout().click();
|
||||
|
||||
// Assert that modern are irc layout are not selected
|
||||
await expect(util.getBubbleLayout()).toBeChecked();
|
||||
await expect(util.getModernLayout()).not.toBeChecked();
|
||||
await expect(util.getIRCLayout()).not.toBeChecked();
|
||||
|
||||
// Assert that the room layout is set to bubble layout
|
||||
await util.assertBubbleLayout();
|
||||
await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-bubble.png");
|
||||
});
|
||||
|
||||
test("should enable compact layout when the modern layout is selected", async ({ page, app, user, util }) => {
|
||||
await expect(util.getCompactLayoutCheckbox()).not.toBeChecked();
|
||||
|
||||
await util.getCompactLayoutCheckbox().click();
|
||||
await util.assertCompactLayout();
|
||||
});
|
||||
|
||||
test("should disable compact layout when the modern layout is not selected", async ({
|
||||
page,
|
||||
app,
|
||||
user,
|
||||
util,
|
||||
}) => {
|
||||
await expect(util.getCompactLayoutCheckbox()).not.toBeDisabled();
|
||||
|
||||
// Select the bubble layout, which should disable the compact layout checkbox
|
||||
await util.getBubbleLayout().click();
|
||||
await expect(util.getCompactLayoutCheckbox()).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Theme Choice Panel", () => {
|
||||
test.beforeEach(async ({ app, user, util }) => {
|
||||
// Disable the default theme for consistency in case ThemeWatcher automatically chooses it
|
||||
|
|
|
@ -14,11 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Page } from "@playwright/test";
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
|
||||
import { ElementAppPage } from "../../../pages/ElementAppPage";
|
||||
import { test as base, expect } from "../../../element-web-test";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import { Layout } from "../../../../src/settings/enums/Layout";
|
||||
|
||||
export { expect };
|
||||
|
||||
|
@ -57,6 +58,21 @@ class Helpers {
|
|||
return this.app.settings.openUserSettings("Appearance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare screenshot and hide the matrix chat
|
||||
* @param locator
|
||||
* @param screenshot
|
||||
*/
|
||||
assertScreenshot(locator: Locator, screenshot: `${string}.png`) {
|
||||
return expect(locator).toMatchScreenshot(screenshot, {
|
||||
css: `
|
||||
#matrixchat {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
// Theme Panel
|
||||
|
||||
/**
|
||||
|
@ -136,4 +152,90 @@ class Helpers {
|
|||
removeCustomTheme() {
|
||||
return this.getThemePanel().getByRole("listitem", { name: this.CUSTOM_THEME.name }).getByRole("button").click();
|
||||
}
|
||||
|
||||
// Message layout Panel
|
||||
|
||||
/**
|
||||
* Create and display a room named Test Room
|
||||
*/
|
||||
async createAndDisplayRoom() {
|
||||
await this.app.client.createRoom({ name: "Test Room" });
|
||||
await this.app.viewRoomByName("Test Room");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the room layout
|
||||
* @param layout
|
||||
* @private
|
||||
*/
|
||||
private assertRoomLayout(layout: Layout) {
|
||||
return expect(this.page.locator(`.mx_RoomView_body[data-layout=${layout}]`)).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the room layout is modern
|
||||
*/
|
||||
assertModernLayout() {
|
||||
return this.assertRoomLayout(Layout.Group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the room layout is bubble
|
||||
*/
|
||||
assertBubbleLayout() {
|
||||
return this.assertRoomLayout(Layout.Bubble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout panel
|
||||
*/
|
||||
getMessageLayoutPanel() {
|
||||
return this.page.getByTestId("layoutPanel");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout radio button
|
||||
* @param layoutName
|
||||
* @private
|
||||
*/
|
||||
private getLayout(layoutName: string) {
|
||||
return this.getMessageLayoutPanel().getByRole("radio", { name: layoutName });
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message bubbles layout radio button
|
||||
*/
|
||||
getBubbleLayout() {
|
||||
return this.getLayout("Message bubbles");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the modern layout radio button
|
||||
*/
|
||||
getModernLayout() {
|
||||
return this.getLayout("Modern");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IRC layout radio button
|
||||
*/
|
||||
getIRCLayout() {
|
||||
return this.getLayout("IRC (experimental)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the compact layout checkbox
|
||||
*/
|
||||
getCompactLayoutCheckbox() {
|
||||
return this.getMessageLayoutPanel().getByRole("checkbox", { name: "Show compact text and messages" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the compact layout is enabled
|
||||
*/
|
||||
assertCompactLayout() {
|
||||
return expect(
|
||||
this.page.locator("#matrixchat .mx_MatrixChat_wrapper.mx_MatrixChat_useCompactLayout"),
|
||||
).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 60 KiB |
|
@ -15,79 +15,80 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_LayoutSwitcher_RadioButtons {
|
||||
.mx_LayoutSwitcher_LayoutSelector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
/**
|
||||
* The settings form has a default gap of 10px
|
||||
* We want to have a bigger gap between the layout options
|
||||
*/
|
||||
gap: var(--cpd-space-4x) !important;
|
||||
|
||||
color: $primary-content;
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio {
|
||||
border: 1px solid var(--cpd-color-border-interactive-primary);
|
||||
border-radius: var(--cpd-space-2x);
|
||||
|
||||
> .mx_LayoutSwitcher_RadioButton {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
flex-basis: 33%;
|
||||
min-width: 0;
|
||||
|
||||
border: 1px solid $quinary-content;
|
||||
border-radius: 10px;
|
||||
|
||||
.mx_EventTile_msgOption,
|
||||
.mx_MessageActionBar {
|
||||
display: none;
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline {
|
||||
display: flex;
|
||||
/*
|
||||
* 10px
|
||||
*/
|
||||
gap: calc(var(--cpd-space-2x) + var(--cpd-space-0-5x));
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_LayoutSwitcher_RadioButton_preview {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline,
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview {
|
||||
margin: var(--cpd-space-3x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the event tile style to make it fit in the selector
|
||||
* Tweak also hover style and remove action bar
|
||||
*/
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview {
|
||||
pointer-events: none;
|
||||
|
||||
.mx_EventTile[data-layout="bubble"] .mx_EventTile_line {
|
||||
padding-right: 11px;
|
||||
.mx_EventTile {
|
||||
margin: 0;
|
||||
|
||||
/**
|
||||
* Hide the message options and message action bar in the preview
|
||||
*/
|
||||
.mx_EventTile_msgOption,
|
||||
.mx_MessageActionBar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_EventTile_content {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&[data-layout="group"] {
|
||||
margin-top: calc(var(--cpd-space-3x) * -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add margin to center the bubble
|
||||
*/
|
||||
&[data-layout="bubble"] {
|
||||
/**
|
||||
* Add the layout margin and the margin to vertically center the bubble
|
||||
*/
|
||||
margin-top: var(--cpd-space-6x);
|
||||
margin-right: 34px;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.mx_EventTile_line {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_StyledRadioButton {
|
||||
flex-grow: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mx_EventTile_content {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.mx_LayoutSwitcher_RadioButton_selected {
|
||||
border-color: var(--cpd-color-bg-accent-rest);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_StyledRadioButton {
|
||||
border-top: 1px solid $quinary-content;
|
||||
}
|
||||
|
||||
.mx_StyledRadioButton_checked {
|
||||
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||
}
|
||||
|
||||
.mx_EventTile {
|
||||
margin: 0;
|
||||
&[data-layout="bubble"] {
|
||||
margin-right: 40px;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
&[data-layout="irc"] {
|
||||
> a {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mx_EventTile_line {
|
||||
max-width: 90%;
|
||||
.mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator {
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid var(--cpd-color-border-interactive-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ interface IProps {
|
|||
/**
|
||||
* classnames to apply to the wrapper of the preview
|
||||
*/
|
||||
className: string;
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The ID of the displayed user
|
||||
|
|
|
@ -1,131 +1,170 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
import React, { JSX, useEffect, useState } from "react";
|
||||
import { Field, HelpMessage, InlineField, Label, RadioControl, Root, ToggleControl } from "@vector-im/compound-web";
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import EventTilePreview from "../elements/EventTilePreview";
|
||||
import StyledRadioButton from "../elements/StyledRadioButton";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { Layout } from "../../../settings/enums/Layout";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import SettingsSubsection from "./shared/SettingsSubsection";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { Layout } from "../../../settings/enums/Layout";
|
||||
import EventTilePreview from "../elements/EventTilePreview";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {
|
||||
userId?: string;
|
||||
displayName?: string;
|
||||
avatarUrl?: string;
|
||||
messagePreviewText: string;
|
||||
onLayoutChanged: (layout: Layout) => void;
|
||||
/**
|
||||
* A section to switch between different message layouts.
|
||||
*/
|
||||
export function LayoutSwitcher(): JSX.Element {
|
||||
return (
|
||||
<SettingsSubsection heading={_t("common|message_layout")} legacy={false} data-testid="layoutPanel">
|
||||
<LayoutSelector />
|
||||
<ToggleCompactLayout />
|
||||
</SettingsSubsection>
|
||||
);
|
||||
}
|
||||
|
||||
interface IState {
|
||||
/**
|
||||
* A selector to choose the layout of the messages.
|
||||
*/
|
||||
function LayoutSelector(): JSX.Element {
|
||||
return (
|
||||
<Root
|
||||
className="mx_LayoutSwitcher_LayoutSelector"
|
||||
onChange={async (evt) => {
|
||||
// We don't have any file in the form, we can cast it as string safely
|
||||
const newLayout = new FormData(evt.currentTarget).get("layout") as string | null;
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, newLayout);
|
||||
}}
|
||||
>
|
||||
<LayoutRadio layout={Layout.Group} label={_t("common|modern")} />
|
||||
<LayoutRadio layout={Layout.Bubble} label={_t("settings|appearance|layout_bubbles")} />
|
||||
<LayoutRadio layout={Layout.IRC} label={_t("settings|appearance|layout_irc")} />
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A radio button to select a layout.
|
||||
*/
|
||||
interface LayoutRadioProps {
|
||||
/**
|
||||
* The value of the layout.
|
||||
*/
|
||||
layout: Layout;
|
||||
/**
|
||||
* The label to display for the layout.
|
||||
*/
|
||||
label: string;
|
||||
}
|
||||
|
||||
export default class LayoutSwitcher extends React.Component<IProps, IState> {
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
/**
|
||||
* A radio button to select a layout.
|
||||
* @param layout
|
||||
* @param label
|
||||
*/
|
||||
function LayoutRadio({ layout, label }: LayoutRadioProps): JSX.Element {
|
||||
const currentLayout = useSettingValue<Layout>("layout");
|
||||
const eventTileInfo = useEventTileInfo();
|
||||
|
||||
this.state = {
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
};
|
||||
}
|
||||
|
||||
private onLayoutChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const layout = e.target.value as Layout;
|
||||
|
||||
this.setState({ layout: layout });
|
||||
SettingsStore.setValue("layout", null, SettingLevel.DEVICE, layout);
|
||||
this.props.onLayoutChanged(layout);
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const ircClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout == Layout.IRC,
|
||||
});
|
||||
const groupClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout == Layout.Group,
|
||||
});
|
||||
const bubbleClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout === Layout.Bubble,
|
||||
});
|
||||
|
||||
return (
|
||||
<SettingsSubsection heading={_t("common|message_layout")}>
|
||||
<div className="mx_LayoutSwitcher_RadioButtons">
|
||||
<label className={ircClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.IRC}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.IRC}
|
||||
checked={this.state.layout === Layout.IRC}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{_t("settings|appearance|layout_irc")}
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={groupClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.Group}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.Group}
|
||||
checked={this.state.layout == Layout.Group}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{_t("common|modern")}
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={bubbleClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.Bubble}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.Bubble}
|
||||
checked={this.state.layout == Layout.Bubble}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{_t("settings|appearance|layout_bubbles")}
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
return (
|
||||
<Field name="layout" className="mxLayoutSwitcher_LayoutSelector_LayoutRadio">
|
||||
<Label aria-label={label}>
|
||||
<div className="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline">
|
||||
<RadioControl name="layout" value={layout} defaultChecked={currentLayout === layout} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
</SettingsSubsection>
|
||||
);
|
||||
}
|
||||
<hr className="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator" />
|
||||
<EventTilePreview
|
||||
message={_t("common|preview_message")}
|
||||
layout={layout}
|
||||
className="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview"
|
||||
{...eventTileInfo}
|
||||
/>
|
||||
</Label>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
type EventTileInfo = {
|
||||
/**
|
||||
* The ID of the user to display.
|
||||
*/
|
||||
userId: string;
|
||||
/**
|
||||
* The display name of the user to display.
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The avatar URL of the user to display.
|
||||
*/
|
||||
avatarUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the information to display in the event tile preview.
|
||||
*/
|
||||
function useEventTileInfo(): EventTileInfo {
|
||||
const matrixClient = useMatrixClientContext();
|
||||
const userId = matrixClient.getSafeUserId();
|
||||
const [eventTileInfo, setEventTileInfo] = useState<EventTileInfo>({ userId });
|
||||
|
||||
useEffect(() => {
|
||||
const run = async (): Promise<void> => {
|
||||
const profileInfo = await matrixClient.getProfileInfo(userId);
|
||||
setEventTileInfo({
|
||||
userId,
|
||||
displayName: profileInfo.displayname,
|
||||
avatarUrl: profileInfo.avatar_url,
|
||||
});
|
||||
};
|
||||
|
||||
run();
|
||||
}, [userId, matrixClient, setEventTileInfo]);
|
||||
return eventTileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* A toggleable setting to enable or disable the compact layout.
|
||||
*/
|
||||
function ToggleCompactLayout(): JSX.Element {
|
||||
const compactLayoutEnabled = useSettingValue<boolean>("useCompactLayout");
|
||||
const layout = useSettingValue<Layout>("layout");
|
||||
|
||||
return (
|
||||
<Root
|
||||
onChange={async (evt) => {
|
||||
const checked = new FormData(evt.currentTarget).get("compactLayout") === "on";
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, checked);
|
||||
}}
|
||||
>
|
||||
<InlineField
|
||||
name="compactLayout"
|
||||
control={
|
||||
<ToggleControl
|
||||
disabled={layout !== Layout.Group}
|
||||
name="compactLayout"
|
||||
defaultChecked={compactLayoutEnabled}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>{_t("settings|appearance|compact_layout")}</Label>
|
||||
<HelpMessage>{_t("settings|appearance|compact_layout_description")}</HelpMessage>
|
||||
</InlineField>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,15 +25,13 @@ import Field from "../../../elements/Field";
|
|||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import { UIFeature } from "../../../../../settings/UIFeature";
|
||||
import { Layout } from "../../../../../settings/enums/Layout";
|
||||
import LayoutSwitcher from "../../LayoutSwitcher";
|
||||
import { LayoutSwitcher } from "../../LayoutSwitcher";
|
||||
import FontScalingPanel from "../../FontScalingPanel";
|
||||
import { ThemeChoicePanel } from "../../ThemeChoicePanel";
|
||||
import ImageSizePanel from "../../ImageSizePanel";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import SettingsSubsection from "../../shared/SettingsSubsection";
|
||||
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {}
|
||||
|
||||
|
@ -42,21 +40,9 @@ interface IState {
|
|||
useSystemFont: boolean;
|
||||
systemFont: string;
|
||||
showAdvanced: boolean;
|
||||
layout: Layout;
|
||||
// User profile data for the message preview
|
||||
userId?: string;
|
||||
displayName?: string;
|
||||
avatarUrl?: string;
|
||||
}
|
||||
|
||||
export default class AppearanceUserSettingsTab extends React.Component<IProps, IState> {
|
||||
public static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
private readonly MESSAGE_PREVIEW_TEXT = _t("common|preview_message");
|
||||
|
||||
private unmounted = false;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -65,32 +51,9 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
useSystemFont: SettingsStore.getValue("useSystemFont"),
|
||||
systemFont: SettingsStore.getValue("systemFont"),
|
||||
showAdvanced: false,
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
};
|
||||
}
|
||||
|
||||
public async componentDidMount(): Promise<void> {
|
||||
// Fetch the current user profile for the message preview
|
||||
const client = this.context;
|
||||
const userId = client.getUserId()!;
|
||||
const profileInfo = await client.getProfileInfo(userId);
|
||||
if (this.unmounted) return;
|
||||
|
||||
this.setState({
|
||||
userId,
|
||||
displayName: profileInfo.displayname,
|
||||
avatarUrl: profileInfo.avatar_url,
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
this.unmounted = true;
|
||||
}
|
||||
|
||||
private onLayoutChanged = (layout: Layout): void => {
|
||||
this.setState({ layout: layout });
|
||||
};
|
||||
|
||||
private renderAdvancedSection(): ReactNode {
|
||||
if (!SettingsStore.getValue(UIFeature.AdvancedSettings)) return null;
|
||||
|
||||
|
@ -156,13 +119,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
<SettingsTab data-testid="mx_AppearanceUserSettingsTab">
|
||||
<SettingsSection>
|
||||
<ThemeChoicePanel />
|
||||
<LayoutSwitcher
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
messagePreviewText={this.MESSAGE_PREVIEW_TEXT}
|
||||
onLayoutChanged={this.onLayoutChanged}
|
||||
/>
|
||||
<LayoutSwitcher />
|
||||
<FontScalingPanel />
|
||||
{this.renderAdvancedSection()}
|
||||
<ImageSizePanel />
|
||||
|
|
|
@ -2416,6 +2416,8 @@
|
|||
"always_show_message_timestamps": "Always show message timestamps",
|
||||
"appearance": {
|
||||
"bundled_emoji_font": "Use bundled emoji font",
|
||||
"compact_layout": "Show compact text and messages",
|
||||
"compact_layout_description": "Modern layout must be selected to use this feature.",
|
||||
"custom_font": "Use a system font",
|
||||
"custom_font_description": "Set the name of a font installed on your system & %(brand)s will attempt to use it.",
|
||||
"custom_font_name": "System font name",
|
||||
|
@ -2432,7 +2434,7 @@
|
|||
"image_size_default": "Default",
|
||||
"image_size_large": "Large",
|
||||
"layout_bubbles": "Message bubbles",
|
||||
"layout_irc": "IRC (Experimental)",
|
||||
"layout_irc": "IRC (experimental)",
|
||||
"match_system_theme": "Match system theme",
|
||||
"timeline_image_size": "Image size in the timeline"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, render, screen, waitFor } from "@testing-library/react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { LayoutSwitcher } from "../../../../src/components/views/settings/LayoutSwitcher";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { stubClient } from "../../../test-utils";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import { Layout } from "../../../../src/settings/enums/Layout";
|
||||
|
||||
describe("<LayoutSwitcher />", () => {
|
||||
const matrixClient = stubClient();
|
||||
const profileInfo = {
|
||||
displayname: "Alice",
|
||||
};
|
||||
|
||||
async function renderLayoutSwitcher() {
|
||||
const renderResult = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<LayoutSwitcher />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
// Wait for the profile info to be displayed in the event tile preview
|
||||
// Also avoid act warning
|
||||
await waitFor(() => expect(screen.getAllByText(profileInfo.displayname).length).toBe(3));
|
||||
return renderResult;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
|
||||
mocked(matrixClient).getProfileInfo.mockResolvedValue(profileInfo);
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
const { asFragment } = await renderLayoutSwitcher();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("layout selection", () => {
|
||||
it("should display the modern layout", async () => {
|
||||
await renderLayoutSwitcher();
|
||||
expect(screen.getByRole("radio", { name: "Modern" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("should change the layout when selected", async () => {
|
||||
await renderLayoutSwitcher();
|
||||
act(() => screen.getByRole("radio", { name: "Message bubbles" }).click());
|
||||
|
||||
expect(screen.getByRole("radio", { name: "Message bubbles" })).toBeChecked();
|
||||
await waitFor(() => expect(SettingsStore.getValue<boolean>("layout")).toBe(Layout.Bubble));
|
||||
});
|
||||
});
|
||||
|
||||
describe("compact layout", () => {
|
||||
beforeEach(async () => {
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, false);
|
||||
});
|
||||
|
||||
it("should be enabled", async () => {
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
|
||||
await renderLayoutSwitcher();
|
||||
|
||||
expect(screen.getByRole("checkbox", { name: "Show compact text and messages" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("should change the setting when toggled", async () => {
|
||||
await renderLayoutSwitcher();
|
||||
act(() => screen.getByRole("checkbox", { name: "Show compact text and messages" }).click());
|
||||
|
||||
await waitFor(() => expect(SettingsStore.getValue<boolean>("useCompactLayout")).toBe(true));
|
||||
});
|
||||
|
||||
it("should be disabled when the modern layout is not enabled", async () => {
|
||||
await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
|
||||
await renderLayoutSwitcher();
|
||||
expect(screen.getByRole("checkbox", { name: "Show compact text and messages" })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,426 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<LayoutSwitcher /> should render 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="layoutPanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Message layout
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<form
|
||||
class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector"
|
||||
>
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<label
|
||||
aria-label="Modern"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-0"
|
||||
>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-0"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="group"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
Modern
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="group"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
Alice
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 30px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<label
|
||||
aria-label="Message bubbles"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-1"
|
||||
>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-1"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="bubble"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
Message bubbles
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="bubble"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
Alice
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 30px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<label
|
||||
aria-label="IRC (experimental)"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-2"
|
||||
>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-2"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="irc"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
IRC (experimental)
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview mx_IRCLayout"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="irc"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
Alice
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 14px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_dgy0u_24"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_dgy0u_40"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_dgy0u_52"
|
||||
>
|
||||
<div
|
||||
class="_container_qnvru_18"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radix-3"
|
||||
class="_input_qnvru_32"
|
||||
id="radix-4"
|
||||
name="compactLayout"
|
||||
title=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_qnvru_42"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_dgy0u_46"
|
||||
>
|
||||
<label
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-4"
|
||||
>
|
||||
Show compact text and messages
|
||||
</label>
|
||||
<span
|
||||
class="_message_dgy0u_98 _help-message_dgy0u_104"
|
||||
id="radix-3"
|
||||
>
|
||||
Modern layout must be selected to use this feature.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -146,143 +146,424 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="layoutPanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Message layout
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<div
|
||||
class="mx_LayoutSwitcher_RadioButtons"
|
||||
<form
|
||||
class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector"
|
||||
>
|
||||
<label
|
||||
class="mx_LayoutSwitcher_RadioButton"
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<div
|
||||
class="mx_LayoutSwitcher_RadioButton_preview mx_IRCLayout mx_EventTilePreview_loader"
|
||||
<label
|
||||
aria-label="Modern"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-3"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-3"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="group"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
Modern
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="group"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
@userId:matrix.org
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 30px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
u
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<label
|
||||
aria-label="Message bubbles"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-4"
|
||||
>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-4"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="bubble"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
Message bubbles
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="bubble"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
@userId:matrix.org
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 30px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
u
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
|
||||
>
|
||||
<label
|
||||
aria-label="IRC (experimental)"
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-5"
|
||||
>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_inline"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-5"
|
||||
name="layout"
|
||||
title=""
|
||||
type="radio"
|
||||
value="irc"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
IRC (experimental)
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_separator"
|
||||
/>
|
||||
<div
|
||||
class="mxLayoutSwitcher_LayoutSelector_LayoutRadio_EventTilePreview mx_IRCLayout"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
aria-atomic="true"
|
||||
aria-live="off"
|
||||
class="mx_EventTile"
|
||||
data-event-id="$9999999999999999999999999999999999999999999"
|
||||
data-has-reply="false"
|
||||
data-layout="irc"
|
||||
data-scroll-tokens="$9999999999999999999999999999999999999999999"
|
||||
data-self="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DisambiguatedProfile"
|
||||
>
|
||||
<span
|
||||
class="mx_Username_color2 mx_DisambiguatedProfile_displayName"
|
||||
dir="auto"
|
||||
>
|
||||
@userId:matrix.org
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_avatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 14px;"
|
||||
title="@userId:matrix.org"
|
||||
>
|
||||
u
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EventTile_line"
|
||||
>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<span
|
||||
class="mx_EventTile_body"
|
||||
dir="auto"
|
||||
>
|
||||
Hey you. You're the best!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message Actions"
|
||||
aria-live="off"
|
||||
class="mx_MessageActionBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<div
|
||||
aria-label="Edit"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_MessageActionBar_iconButton mx_MessageActionBar_optionsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_dgy0u_24"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_dgy0u_40"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_dgy0u_52"
|
||||
>
|
||||
<div
|
||||
class="_container_qnvru_18"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radix-6"
|
||||
class="_input_qnvru_32"
|
||||
id="radix-7"
|
||||
name="compactLayout"
|
||||
title=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_qnvru_42"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
|
||||
>
|
||||
<input
|
||||
name="layout"
|
||||
type="radio"
|
||||
value="irc"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
IRC (Experimental)
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
</label>
|
||||
<label
|
||||
class="mx_LayoutSwitcher_RadioButton mx_LayoutSwitcher_RadioButton_selected"
|
||||
>
|
||||
<div
|
||||
class="mx_LayoutSwitcher_RadioButton_preview mx_EventTilePreview_loader"
|
||||
class="_inline-field-body_dgy0u_46"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
<label
|
||||
class="_label_dgy0u_67"
|
||||
for="radix-7"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
Show compact text and messages
|
||||
</label>
|
||||
<span
|
||||
class="_message_dgy0u_98 _help-message_dgy0u_104"
|
||||
id="radix-6"
|
||||
>
|
||||
Modern layout must be selected to use this feature.
|
||||
</span>
|
||||
</div>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
name="layout"
|
||||
type="radio"
|
||||
value="group"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Modern
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
</label>
|
||||
<label
|
||||
class="mx_LayoutSwitcher_RadioButton"
|
||||
>
|
||||
<div
|
||||
class="mx_LayoutSwitcher_RadioButton_preview mx_EventTilePreview_loader"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
|
||||
>
|
||||
<input
|
||||
name="layout"
|
||||
type="radio"
|
||||
value="bubble"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Message bubbles
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
|
|
Loading…
Reference in New Issue