New theme ui in user settings (#12576)
* Add hook to get the theme * Adapt subsection settings to new ui * WIP new theme subsection * Add theme selection * Fix test types * Disabled theme selector when system theme is used * Update compound to `4.4.1` * Add custom theme support * Remove old ThemChoicePanel * Fix QuickThemeSwitcher-test.tsx * Fix AppearanceUserSettingsTab-test.tsx * Update i18n * Fix ThemeChoicePanel-test.tsx * Update `@vector-im/compound-web` * Small tweaks * Fix CSS comments and use compound variable * Remove custom theme title * i18n: update * test: add tests to theme selection * test: update AppearanceUserSettingsTab-test snapshot * test: rework custom theme * playwright: fix audio-player.spec.ts * playwright: appearance tab * test: update snapshot * playright: add custom theme * i18n: use correct char for ellipsis * a11y: add missing aria-label to delete button * dialog: update close button tooltip * theme: remove local state and handle custom delete * theme: don't add twice the same custom theme * test: update snapshot * playwright: update snapshot * custom theme: add background to custom theme list * update compound web * Use new destructive property on `IconButton` of theme panel * test: update snapshots * rename new ui into legacy * remove wrong constructor doc * fix theme selector padding * theme selector: fix key * test: fix e2e
This commit is contained in:
parent
8ede89101a
commit
33a017b528
30 changed files with 1749 additions and 477 deletions
|
@ -15,15 +15,177 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { act, render, screen, waitFor } from "@testing-library/react";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import * as TestUtils from "../../../test-utils";
|
||||
import ThemeChoicePanel from "../../../../src/components/views/settings/ThemeChoicePanel";
|
||||
import { ThemeChoicePanel } from "../../../../src/components/views/settings/ThemeChoicePanel";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import ThemeWatcher from "../../../../src/settings/watchers/ThemeWatcher";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
|
||||
jest.mock("../../../../src/settings/watchers/ThemeWatcher");
|
||||
|
||||
describe("<ThemeChoicePanel />", () => {
|
||||
/**
|
||||
* Enable or disable the system theme
|
||||
* @param enable
|
||||
*/
|
||||
async function enableSystemTheme(enable: boolean) {
|
||||
await SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the theme
|
||||
* @param theme
|
||||
*/
|
||||
async function setTheme(theme: string) {
|
||||
await SettingsStore.setValue("theme", null, SettingLevel.DEVICE, theme);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
mocked(ThemeWatcher).mockImplementation(() => {
|
||||
return {
|
||||
isSystemThemeSupported: jest.fn().mockReturnValue(true),
|
||||
} as unknown as MockedObject<ThemeWatcher>;
|
||||
});
|
||||
|
||||
await enableSystemTheme(false);
|
||||
await setTheme("light");
|
||||
});
|
||||
|
||||
describe("ThemeChoicePanel", () => {
|
||||
it("renders the theme choice UI", () => {
|
||||
TestUtils.stubClient();
|
||||
const { asFragment } = render(<ThemeChoicePanel />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("theme selection", () => {
|
||||
describe("system theme", () => {
|
||||
it("should disable Match system theme", async () => {
|
||||
render(<ThemeChoicePanel />);
|
||||
expect(screen.getByRole("checkbox", { name: "Match system theme" })).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("should enable Match system theme", async () => {
|
||||
await enableSystemTheme(true);
|
||||
|
||||
render(<ThemeChoicePanel />);
|
||||
expect(screen.getByRole("checkbox", { name: "Match system theme" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("should change the system theme when clicked", async () => {
|
||||
jest.spyOn(SettingsStore, "setValue");
|
||||
|
||||
render(<ThemeChoicePanel />);
|
||||
act(() => screen.getByRole("checkbox", { name: "Match system theme" }).click());
|
||||
|
||||
// The system theme should be enabled
|
||||
expect(screen.getByRole("checkbox", { name: "Match system theme" })).toBeChecked();
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("use_system_theme", null, "device", true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("theme selection", () => {
|
||||
it("should disable theme selection when system theme is enabled", async () => {
|
||||
await enableSystemTheme(true);
|
||||
render(<ThemeChoicePanel />);
|
||||
|
||||
// We expect all the themes to be disabled
|
||||
const themes = screen.getAllByRole("radio");
|
||||
themes.forEach((theme) => {
|
||||
expect(theme).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should enable theme selection when system theme is disabled", async () => {
|
||||
render(<ThemeChoicePanel />);
|
||||
|
||||
// We expect all the themes to be disabled
|
||||
const themes = screen.getAllByRole("radio");
|
||||
themes.forEach((theme) => {
|
||||
expect(theme).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should have light theme selected", async () => {
|
||||
render(<ThemeChoicePanel />);
|
||||
|
||||
// We expect the light theme to be selected
|
||||
const lightTheme = screen.getByRole("radio", { name: "Light" });
|
||||
expect(lightTheme).toBeChecked();
|
||||
|
||||
// And the dark theme shouldn't be selected
|
||||
const darkTheme = screen.getByRole("radio", { name: "Dark" });
|
||||
expect(darkTheme).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("should switch to dark theme", async () => {
|
||||
jest.spyOn(SettingsStore, "setValue");
|
||||
|
||||
render(<ThemeChoicePanel />);
|
||||
|
||||
const darkTheme = screen.getByRole("radio", { name: "Dark" });
|
||||
const lightTheme = screen.getByRole("radio", { name: "Light" });
|
||||
expect(darkTheme).not.toBeChecked();
|
||||
|
||||
// Switch to the dark theme
|
||||
act(() => darkTheme.click());
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("theme", null, "device", "dark");
|
||||
|
||||
// Dark theme is now selected
|
||||
await waitFor(() => expect(darkTheme).toBeChecked());
|
||||
// Light theme is not selected anymore
|
||||
expect(lightTheme).not.toBeChecked();
|
||||
// The setting should be updated
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("theme", null, "device", "dark");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom theme", () => {
|
||||
const aliceTheme = { name: "Alice theme", is_dark: true, colors: {} };
|
||||
const bobTheme = { name: "Bob theme", is_dark: false, colors: {} };
|
||||
|
||||
beforeEach(async () => {
|
||||
await SettingsStore.setValue("feature_custom_themes", null, SettingLevel.DEVICE, true);
|
||||
await SettingsStore.setValue("custom_themes", null, SettingLevel.DEVICE, [aliceTheme]);
|
||||
});
|
||||
|
||||
it("should render the custom theme section", () => {
|
||||
const { asFragment } = render(<ThemeChoicePanel />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should add a custom theme", async () => {
|
||||
jest.spyOn(SettingsStore, "setValue");
|
||||
// Respond to the theme request
|
||||
fetchMock.get("http://bob.theme", {
|
||||
body: bobTheme,
|
||||
});
|
||||
|
||||
render(<ThemeChoicePanel />);
|
||||
|
||||
// Add the new custom theme
|
||||
const customThemeInput = screen.getByRole("textbox", { name: "Add custom theme" });
|
||||
await userEvent.type(customThemeInput, "http://bob.theme");
|
||||
screen.getByRole("button", { name: "Add custom theme" }).click();
|
||||
|
||||
// The new custom theme is added to the user's themes
|
||||
await waitFor(() =>
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("custom_themes", null, "account", [
|
||||
aliceTheme,
|
||||
bobTheme,
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should display custom theme", () => {
|
||||
const { asFragment } = render(<ThemeChoicePanel />);
|
||||
|
||||
expect(screen.getByRole("radio", { name: aliceTheme.name })).toBeInTheDocument();
|
||||
expect(screen.getByRole("listitem", { name: aliceTheme.name })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,73 +1,774 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ThemeChoicePanel renders the theme choice UI 1`] = `
|
||||
exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
data-testid="mx_ThemeChoicePanel"
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="themePanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Theme
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<div
|
||||
class="mx_ThemeChoicePanel_themeSelectors"
|
||||
data-testid="theme-choice-panel-selectors"
|
||||
<form
|
||||
class="_root_148br_24"
|
||||
>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_ThemeSelector_light mx_StyledRadioButton_disabled mx_StyledRadioButton_outlined"
|
||||
<div
|
||||
class="_inline-field_148br_40"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
id="theme-light"
|
||||
name="theme"
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
Light
|
||||
<div
|
||||
class="_container_qnvru_18"
|
||||
>
|
||||
<input
|
||||
class="_input_qnvru_32"
|
||||
id="radix-42"
|
||||
name="systemTheme"
|
||||
title=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_qnvru_42"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_ThemeSelector_dark mx_StyledRadioButton_disabled mx_StyledRadioButton_outlined"
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67"
|
||||
for="radix-42"
|
||||
>
|
||||
Match system theme
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_148br_24 mx_ThemeChoicePanel_ThemeSelectors"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
id="theme-dark"
|
||||
name="theme"
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
Dark
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-43"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-43"
|
||||
>
|
||||
Light
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-44"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-44"
|
||||
>
|
||||
Dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-45"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light-high-contrast"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-45"
|
||||
>
|
||||
High contrast
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-46"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="custom-Alice theme"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-46"
|
||||
>
|
||||
Alice theme
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
class="mx_ThemeChoicePanel_CustomTheme"
|
||||
>
|
||||
<form
|
||||
class="_container_zfn7i_17 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
|
||||
id=":r7:"
|
||||
>
|
||||
<div
|
||||
class="_label_zfn7i_21"
|
||||
id=":r8:"
|
||||
>
|
||||
Add custom theme
|
||||
</div>
|
||||
<div
|
||||
class="_controls_zfn7i_27"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
aria-labelledby=":r8:"
|
||||
class="_control_9gon8_18 _control_zfn7i_27"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="_button-group_zfn7i_32"
|
||||
>
|
||||
<button
|
||||
aria-controls=":r7:"
|
||||
aria-label="Add custom theme"
|
||||
class="_button_zfn7i_32 _primary-button_zfn7i_51"
|
||||
type="submit"
|
||||
>
|
||||
<svg
|
||||
class="cpd-icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.878.878 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.712-.275s.53.092.713.275c.183.183.275.42.275.712s-.092.53-.275.713l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
aria-controls=":r7:"
|
||||
class="_button_zfn7i_32"
|
||||
role="button"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
class="cpd-icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="_caption-line_zfn7i_92 _caption-text_zfn7i_130 _caption-text-help_zfn7i_147"
|
||||
>
|
||||
Enter the URL of a custom theme you want to apply.
|
||||
</span>
|
||||
</form>
|
||||
<ul
|
||||
class="mx_ThemeChoicePanel_CustomThemeList"
|
||||
>
|
||||
<li
|
||||
aria-label="Alice theme"
|
||||
class="mx_ThemeChoicePanel_CustomThemeList_theme"
|
||||
>
|
||||
<span
|
||||
class="mx_ThemeChoicePanel_CustomThemeList_name"
|
||||
>
|
||||
Alice theme
|
||||
</span>
|
||||
<button
|
||||
aria-label="Delete"
|
||||
class="_icon-button_rijzz_17 _destructive_rijzz_78"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ThemeChoicePanel /> custom theme should render the custom theme section 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="themePanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Theme
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<form
|
||||
class="_root_148br_24"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_148br_40"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_qnvru_18"
|
||||
>
|
||||
<input
|
||||
class="_input_qnvru_32"
|
||||
id="radix-32"
|
||||
name="systemTheme"
|
||||
title=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_qnvru_42"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67"
|
||||
for="radix-32"
|
||||
>
|
||||
Match system theme
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_148br_24 mx_ThemeChoicePanel_ThemeSelectors"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-33"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-33"
|
||||
>
|
||||
Light
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-34"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-34"
|
||||
>
|
||||
Dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-35"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light-high-contrast"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-35"
|
||||
>
|
||||
High contrast
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-36"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="custom-Alice theme"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-36"
|
||||
>
|
||||
Alice theme
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
class="mx_ThemeChoicePanel_CustomTheme"
|
||||
>
|
||||
<form
|
||||
class="_container_zfn7i_17 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
|
||||
id=":r1:"
|
||||
>
|
||||
<div
|
||||
class="_label_zfn7i_21"
|
||||
id=":r2:"
|
||||
>
|
||||
Add custom theme
|
||||
</div>
|
||||
<div
|
||||
class="_controls_zfn7i_27"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
aria-labelledby=":r2:"
|
||||
class="_control_9gon8_18 _control_zfn7i_27"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="_button-group_zfn7i_32"
|
||||
>
|
||||
<button
|
||||
aria-controls=":r1:"
|
||||
aria-label="Add custom theme"
|
||||
class="_button_zfn7i_32 _primary-button_zfn7i_51"
|
||||
type="submit"
|
||||
>
|
||||
<svg
|
||||
class="cpd-icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.878.878 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.712-.275s.53.092.713.275c.183.183.275.42.275.712s-.092.53-.275.713l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
aria-controls=":r1:"
|
||||
class="_button_zfn7i_32"
|
||||
role="button"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
class="cpd-icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="_caption-line_zfn7i_92 _caption-text_zfn7i_130 _caption-text-help_zfn7i_147"
|
||||
>
|
||||
Enter the URL of a custom theme you want to apply.
|
||||
</span>
|
||||
</form>
|
||||
<ul
|
||||
class="mx_ThemeChoicePanel_CustomThemeList"
|
||||
>
|
||||
<li
|
||||
aria-label="Alice theme"
|
||||
class="mx_ThemeChoicePanel_CustomThemeList_theme"
|
||||
>
|
||||
<span
|
||||
class="mx_ThemeChoicePanel_CustomThemeList_name"
|
||||
>
|
||||
Alice theme
|
||||
</span>
|
||||
<button
|
||||
aria-label="Delete"
|
||||
class="_icon-button_rijzz_17 _destructive_rijzz_78"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="themePanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Theme
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<form
|
||||
class="_root_148br_24"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_148br_40"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_qnvru_18"
|
||||
>
|
||||
<input
|
||||
class="_input_qnvru_32"
|
||||
id="radix-0"
|
||||
name="systemTheme"
|
||||
title=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_qnvru_42"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67"
|
||||
for="radix-0"
|
||||
>
|
||||
Match system theme
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_148br_24 mx_ThemeChoicePanel_ThemeSelectors"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-1"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-1"
|
||||
>
|
||||
Light
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-2"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-2"
|
||||
>
|
||||
Dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
id="radix-3"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light-high-contrast"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-3"
|
||||
>
|
||||
High contrast
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -64,8 +64,13 @@ describe("PreferencesUserSettingsTab", () => {
|
|||
const mockGetValue = (val: boolean) => {
|
||||
const copyOfGetValueAt = SettingsStore.getValueAt;
|
||||
|
||||
SettingsStore.getValueAt = (level: SettingLevel, name: string, roomId?: string, isExplicit?: boolean) => {
|
||||
if (name === "sendReadReceipts") return val;
|
||||
SettingsStore.getValueAt = <T,>(
|
||||
level: SettingLevel,
|
||||
name: string,
|
||||
roomId?: string,
|
||||
isExplicit?: boolean,
|
||||
): T => {
|
||||
if (name === "sendReadReceipts") return val as T;
|
||||
return copyOfGetValueAt(level, name, roomId, isExplicit);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,71 +16,134 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
|
|||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
data-testid="mx_ThemeChoicePanel"
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
data-testid="themePanel"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Theme
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<div
|
||||
class="mx_ThemeChoicePanel_themeSelectors"
|
||||
data-testid="theme-choice-panel-selectors"
|
||||
<form
|
||||
class="_root_148br_24 mx_ThemeChoicePanel_ThemeSelectors"
|
||||
>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_ThemeSelector_light mx_StyledRadioButton_disabled mx_StyledRadioButton_outlined"
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
id="theme-light"
|
||||
name="theme"
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
Light
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
disabled=""
|
||||
id="radix-0"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_ThemeSelector_dark mx_StyledRadioButton_disabled mx_StyledRadioButton_outlined"
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-0"
|
||||
>
|
||||
Light
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-dark"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
id="theme-dark"
|
||||
name="theme"
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
Dark
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
disabled=""
|
||||
id="radix-1"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="dark"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-1"
|
||||
>
|
||||
Dark
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_148br_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_148br_52"
|
||||
>
|
||||
<div
|
||||
class="_container_1vw5h_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1vw5h_26"
|
||||
disabled=""
|
||||
id="radix-2"
|
||||
name="themeSelector"
|
||||
title=""
|
||||
type="radio"
|
||||
value="light-high-contrast"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1vw5h_27"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_148br_46"
|
||||
>
|
||||
<label
|
||||
class="_label_148br_67 mx_ThemeChoicePanel_themeSelector_Label"
|
||||
for="radix-2"
|
||||
>
|
||||
High contrast
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
|
|
|
@ -21,17 +21,17 @@ import { mocked } from "jest-mock";
|
|||
|
||||
import QuickThemeSwitcher from "../../../../src/components/views/spaces/QuickThemeSwitcher";
|
||||
import { getOrderedThemes } from "../../../../src/theme";
|
||||
import ThemeChoicePanel from "../../../../src/components/views/settings/ThemeChoicePanel";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import dis from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { mockPlatformPeg } from "../../../test-utils/platform";
|
||||
import { useTheme } from "../../../../src/hooks/useTheme";
|
||||
|
||||
jest.mock("../../../../src/theme");
|
||||
jest.mock("../../../../src/components/views/settings/ThemeChoicePanel", () => ({
|
||||
calculateThemeState: jest.fn(),
|
||||
jest.mock("../../../../src/hooks/useTheme", () => ({
|
||||
useTheme: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../src/theme");
|
||||
jest.mock("../../../../src/settings/SettingsStore", () => ({
|
||||
setValue: jest.fn(),
|
||||
getValue: jest.fn(),
|
||||
|
@ -59,9 +59,10 @@ describe("<QuickThemeSwitcher />", () => {
|
|||
{ id: "light", name: "Light" },
|
||||
{ id: "dark", name: "Dark" },
|
||||
]);
|
||||
mocked(ThemeChoicePanel).calculateThemeState.mockClear().mockReturnValue({
|
||||
|
||||
mocked(useTheme).mockClear().mockReturnValue({
|
||||
theme: "light",
|
||||
useSystemTheme: false,
|
||||
systemThemeActivated: false,
|
||||
});
|
||||
mocked(SettingsStore).setValue.mockClear().mockResolvedValue();
|
||||
mocked(dis).dispatch.mockClear();
|
||||
|
@ -85,9 +86,9 @@ describe("<QuickThemeSwitcher />", () => {
|
|||
});
|
||||
|
||||
it("renders dropdown correctly when use system theme is truthy", () => {
|
||||
mocked(ThemeChoicePanel).calculateThemeState.mockClear().mockReturnValue({
|
||||
mocked(useTheme).mockClear().mockReturnValue({
|
||||
theme: "light",
|
||||
useSystemTheme: true,
|
||||
systemThemeActivated: true,
|
||||
});
|
||||
renderComponent();
|
||||
expect(screen.getByText("Match system")).toBeInTheDocument();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue