Move language settings to 'preferences' (#12723)
* Move language settings to 'preferences' Their new home is in this tab * Update snapshot * Move playwright test code * Add test * tests * Update screenshot
This commit is contained in:
parent
dcf7643d4a
commit
81f29d13dc
9 changed files with 198 additions and 110 deletions
|
@ -73,24 +73,6 @@ test.describe("General user settings tab", () => {
|
||||||
// Assert that the add button is rendered
|
// Assert that the add button is rendered
|
||||||
await expect(phoneNumbers.getByRole("button", { name: "Add" })).toBeVisible();
|
await expect(phoneNumbers.getByRole("button", { name: "Add" })).toBeVisible();
|
||||||
|
|
||||||
// Check language and region setting dropdown
|
|
||||||
const languageInput = uut.locator(".mx_GeneralUserSettingsTab_section_languageInput");
|
|
||||||
await languageInput.scrollIntoViewIfNeeded();
|
|
||||||
// Check the default value
|
|
||||||
await expect(languageInput.getByText("English")).toBeVisible();
|
|
||||||
// Click the button to display the dropdown menu
|
|
||||||
await languageInput.getByRole("button", { name: "Language Dropdown" }).click();
|
|
||||||
// Assert that the default option is rendered and highlighted
|
|
||||||
languageInput.getByRole("option", { name: /Albanian/ });
|
|
||||||
await expect(languageInput.getByRole("option", { name: /Albanian/ })).toHaveClass(
|
|
||||||
/mx_Dropdown_option_highlight/,
|
|
||||||
);
|
|
||||||
await expect(languageInput.getByRole("option", { name: /Deutsch/ })).toBeVisible();
|
|
||||||
// Click again to close the dropdown
|
|
||||||
await languageInput.getByRole("button", { name: "Language Dropdown" }).click();
|
|
||||||
// Assert that the default value is rendered again
|
|
||||||
await expect(languageInput.getByText("English")).toBeVisible();
|
|
||||||
|
|
||||||
const setIntegrationManager = uut.locator(".mx_SetIntegrationManager");
|
const setIntegrationManager = uut.locator(".mx_SetIntegrationManager");
|
||||||
await setIntegrationManager.scrollIntoViewIfNeeded();
|
await setIntegrationManager.scrollIntoViewIfNeeded();
|
||||||
await expect(
|
await expect(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2023 Suguru Hirahara
|
Copyright 2023 Suguru Hirahara
|
||||||
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (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 not use this file except in compliance with the License.
|
||||||
|
@ -19,6 +20,10 @@ import { test, expect } from "../../element-web-test";
|
||||||
test.describe("Preferences user settings tab", () => {
|
test.describe("Preferences user settings tab", () => {
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Bob",
|
displayName: "Bob",
|
||||||
|
uut: async ({ app, user }, use) => {
|
||||||
|
const locator = await app.settings.openUserSettings("Preferences");
|
||||||
|
await use(locator);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should be rendered properly", async ({ app, user }) => {
|
test("should be rendered properly", async ({ app, user }) => {
|
||||||
|
@ -28,4 +33,24 @@ test.describe("Preferences user settings tab", () => {
|
||||||
await expect(tab.getByRole("heading", { name: "Preferences" })).toBeVisible();
|
await expect(tab.getByRole("heading", { name: "Preferences" })).toBeVisible();
|
||||||
await expect(tab).toMatchScreenshot();
|
await expect(tab).toMatchScreenshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should be able to change the app language", async ({ uut, user }) => {
|
||||||
|
// Check language and region setting dropdown
|
||||||
|
const languageInput = uut.locator(".mx_GeneralUserSettingsTab_section_languageInput");
|
||||||
|
await languageInput.scrollIntoViewIfNeeded();
|
||||||
|
// Check the default value
|
||||||
|
await expect(languageInput.getByText("English")).toBeVisible();
|
||||||
|
// Click the button to display the dropdown menu
|
||||||
|
await languageInput.getByRole("button", { name: "Language Dropdown" }).click();
|
||||||
|
// Assert that the default option is rendered and highlighted
|
||||||
|
languageInput.getByRole("option", { name: /Albanian/ });
|
||||||
|
await expect(languageInput.getByRole("option", { name: /Albanian/ })).toHaveClass(
|
||||||
|
/mx_Dropdown_option_highlight/,
|
||||||
|
);
|
||||||
|
await expect(languageInput.getByRole("option", { name: /Deutsch/ })).toBeVisible();
|
||||||
|
// Click again to close the dropdown
|
||||||
|
await languageInput.getByRole("button", { name: "Language Dropdown" }).click();
|
||||||
|
// Assert that the default value is rendered again
|
||||||
|
await expect(languageInput.getByText("English")).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 54 KiB |
|
@ -34,3 +34,8 @@ limitations under the License.
|
||||||
margin-right: $spacing-8;
|
margin-right: $spacing-8;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_GeneralUserSettingsTab_section_hint {
|
||||||
|
font: var(--cpd-font-body-sm-regular);
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
}
|
||||||
|
|
|
@ -22,25 +22,17 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { UserFriendlyError, _t } from "../../../../../languageHandler";
|
import { UserFriendlyError, _t } from "../../../../../languageHandler";
|
||||||
import UserProfileSettings from "../../UserProfileSettings";
|
import UserProfileSettings from "../../UserProfileSettings";
|
||||||
import * as languageHandler from "../../../../../languageHandler";
|
|
||||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
import LanguageDropdown from "../../../elements/LanguageDropdown";
|
|
||||||
import SpellCheckSettings from "../../SpellCheckSettings";
|
|
||||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
||||||
import PlatformPeg from "../../../../../PlatformPeg";
|
|
||||||
import Modal from "../../../../../Modal";
|
import Modal from "../../../../../Modal";
|
||||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
|
||||||
import { UIFeature } from "../../../../../settings/UIFeature";
|
import { UIFeature } from "../../../../../settings/UIFeature";
|
||||||
import ErrorDialog, { extractErrorMessageFromError } from "../../../dialogs/ErrorDialog";
|
import ErrorDialog, { extractErrorMessageFromError } from "../../../dialogs/ErrorDialog";
|
||||||
import ChangePassword from "../../ChangePassword";
|
import ChangePassword from "../../ChangePassword";
|
||||||
import SetIntegrationManager from "../../SetIntegrationManager";
|
import SetIntegrationManager from "../../SetIntegrationManager";
|
||||||
import ToggleSwitch from "../../../elements/ToggleSwitch";
|
|
||||||
import { IS_MAC } from "../../../../../Keyboard";
|
|
||||||
import SettingsTab from "../SettingsTab";
|
import SettingsTab from "../SettingsTab";
|
||||||
import { SettingsSection } from "../../shared/SettingsSection";
|
import { SettingsSection } from "../../shared/SettingsSection";
|
||||||
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
||||||
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
|
|
||||||
import { SDKContext } from "../../../../../contexts/SDKContext";
|
import { SDKContext } from "../../../../../contexts/SDKContext";
|
||||||
import UserPersonalInfoSettings from "../../UserPersonalInfoSettings";
|
import UserPersonalInfoSettings from "../../UserPersonalInfoSettings";
|
||||||
|
|
||||||
|
@ -49,9 +41,6 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
language: string;
|
|
||||||
spellCheckEnabled?: boolean;
|
|
||||||
spellCheckLanguages: string[];
|
|
||||||
canChangePassword: boolean;
|
canChangePassword: boolean;
|
||||||
idServerName?: string;
|
idServerName?: string;
|
||||||
externalAccountManagementUrl?: string;
|
externalAccountManagementUrl?: string;
|
||||||
|
@ -69,9 +58,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
language: languageHandler.getCurrentLanguage(),
|
|
||||||
spellCheckEnabled: false,
|
|
||||||
spellCheckLanguages: [],
|
|
||||||
canChangePassword: false,
|
canChangePassword: false,
|
||||||
canMake3pidChanges: false,
|
canMake3pidChanges: false,
|
||||||
canSetDisplayName: false,
|
canSetDisplayName: false,
|
||||||
|
@ -81,21 +67,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
this.getCapabilities();
|
this.getCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount(): Promise<void> {
|
|
||||||
const plat = PlatformPeg.get();
|
|
||||||
const [spellCheckEnabled, spellCheckLanguages] = await Promise.all([
|
|
||||||
plat?.getSpellCheckEnabled(),
|
|
||||||
plat?.getSpellCheckLanguages(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (spellCheckLanguages) {
|
|
||||||
this.setState({
|
|
||||||
spellCheckEnabled,
|
|
||||||
spellCheckLanguages,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getCapabilities(): Promise<void> {
|
private async getCapabilities(): Promise<void> {
|
||||||
const cli = this.context.client!;
|
const cli = this.context.client!;
|
||||||
|
|
||||||
|
@ -127,28 +98,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onLanguageChange = (newLanguage: string): void => {
|
|
||||||
if (this.state.language === newLanguage) return;
|
|
||||||
|
|
||||||
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLanguage);
|
|
||||||
this.setState({ language: newLanguage });
|
|
||||||
const platform = PlatformPeg.get();
|
|
||||||
if (platform) {
|
|
||||||
platform.setLanguage([newLanguage]);
|
|
||||||
platform.reload();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private onSpellCheckLanguagesChange = (languages: string[]): void => {
|
|
||||||
this.setState({ spellCheckLanguages: languages });
|
|
||||||
PlatformPeg.get()?.setSpellCheckLanguages(languages);
|
|
||||||
};
|
|
||||||
|
|
||||||
private onSpellCheckEnabledChange = (spellCheckEnabled: boolean): void => {
|
|
||||||
this.setState({ spellCheckEnabled });
|
|
||||||
PlatformPeg.get()?.setSpellCheckEnabled(spellCheckEnabled);
|
|
||||||
};
|
|
||||||
|
|
||||||
private onPasswordChangeError = (err: Error): void => {
|
private onPasswordChangeError = (err: Error): void => {
|
||||||
logger.error("Failed to change password: " + err);
|
logger.error("Failed to change password: " + err);
|
||||||
|
|
||||||
|
@ -228,37 +177,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderLanguageSection(): JSX.Element {
|
|
||||||
// TODO: Convert to new-styled Field
|
|
||||||
return (
|
|
||||||
<SettingsSubsection heading={_t("settings|general|language_section")} stretchContent>
|
|
||||||
<LanguageDropdown
|
|
||||||
className="mx_GeneralUserSettingsTab_section_languageInput"
|
|
||||||
onOptionChange={this.onLanguageChange}
|
|
||||||
value={this.state.language}
|
|
||||||
/>
|
|
||||||
</SettingsSubsection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderSpellCheckSection(): JSX.Element {
|
|
||||||
const heading = (
|
|
||||||
<SettingsSubsectionHeading heading={_t("settings|general|spell_check_section")}>
|
|
||||||
<ToggleSwitch checked={!!this.state.spellCheckEnabled} onChange={this.onSpellCheckEnabledChange} />
|
|
||||||
</SettingsSubsectionHeading>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<SettingsSubsection heading={heading} stretchContent>
|
|
||||||
{this.state.spellCheckEnabled && !IS_MAC && (
|
|
||||||
<SpellCheckSettings
|
|
||||||
languages={this.state.spellCheckLanguages}
|
|
||||||
onLanguagesChange={this.onSpellCheckLanguagesChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</SettingsSubsection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderManagementSection(): JSX.Element {
|
private renderManagementSection(): JSX.Element {
|
||||||
// TODO: Improve warning text for account deactivation
|
// TODO: Improve warning text for account deactivation
|
||||||
return (
|
return (
|
||||||
|
@ -283,9 +201,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const plaf = PlatformPeg.get();
|
|
||||||
const supportsMultiLanguageSpellCheck = plaf?.supportsSpellCheckSettings();
|
|
||||||
|
|
||||||
let accountManagementSection: JSX.Element | undefined;
|
let accountManagementSection: JSX.Element | undefined;
|
||||||
const isAccountManagedExternally = !!this.state.externalAccountManagementUrl;
|
const isAccountManagedExternally = !!this.state.externalAccountManagementUrl;
|
||||||
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
|
if (SettingsStore.getValue(UIFeature.Deactivate) && !isAccountManagedExternally) {
|
||||||
|
@ -302,8 +217,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
/>
|
/>
|
||||||
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
|
<UserPersonalInfoSettings canMake3pidChanges={this.state.canMake3pidChanges} />
|
||||||
{this.renderAccountSection()}
|
{this.renderAccountSection()}
|
||||||
{this.renderLanguageSection()}
|
|
||||||
{supportsMultiLanguageSpellCheck ? this.renderSpellCheckSection() : null}
|
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
{this.renderIntegrationManagerSection()}
|
{this.renderIntegrationManagerSection()}
|
||||||
{accountManagementSection}
|
{accountManagementSection}
|
||||||
|
|
|
@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { _t } from "../../../../../languageHandler";
|
import { _t, getCurrentLanguage } from "../../../../../languageHandler";
|
||||||
import { UseCase } from "../../../../../settings/enums/UseCase";
|
import { UseCase } from "../../../../../settings/enums/UseCase";
|
||||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
import Field from "../../../elements/Field";
|
import Field from "../../../elements/Field";
|
||||||
|
@ -33,6 +33,11 @@ import { showUserOnboardingPage } from "../../../user-onboarding/UserOnboardingP
|
||||||
import SettingsSubsection from "../../shared/SettingsSubsection";
|
import SettingsSubsection from "../../shared/SettingsSubsection";
|
||||||
import SettingsTab from "../SettingsTab";
|
import SettingsTab from "../SettingsTab";
|
||||||
import { SettingsSection } from "../../shared/SettingsSection";
|
import { SettingsSection } from "../../shared/SettingsSection";
|
||||||
|
import LanguageDropdown from "../../../elements/LanguageDropdown";
|
||||||
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
|
import { IS_MAC } from "../../../../../Keyboard";
|
||||||
|
import SpellCheckSettings from "../../SpellCheckSettings";
|
||||||
|
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeSettingsFn(success: boolean): void;
|
closeSettingsFn(success: boolean): void;
|
||||||
|
@ -44,6 +49,79 @@ interface IState {
|
||||||
readMarkerOutOfViewThresholdMs: string;
|
readMarkerOutOfViewThresholdMs: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LanguageSection: React.FC = () => {
|
||||||
|
const [language, setLanguage] = useState(getCurrentLanguage());
|
||||||
|
|
||||||
|
const onLanguageChange = useCallback(
|
||||||
|
(newLanguage: string) => {
|
||||||
|
if (language === newLanguage) return;
|
||||||
|
|
||||||
|
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLanguage);
|
||||||
|
setLanguage(newLanguage);
|
||||||
|
const platform = PlatformPeg.get();
|
||||||
|
if (platform) {
|
||||||
|
platform.setLanguage([newLanguage]);
|
||||||
|
platform.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[language],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_SettingsSubsection_contentStretch">
|
||||||
|
{_t("settings|general|application_language")}
|
||||||
|
<LanguageDropdown
|
||||||
|
className="mx_GeneralUserSettingsTab_section_languageInput"
|
||||||
|
onOptionChange={onLanguageChange}
|
||||||
|
value={language}
|
||||||
|
/>
|
||||||
|
<div className="mx_GeneralUserSettingsTab_section_hint">
|
||||||
|
{_t("settings|general|application_language_reload_hint")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SpellCheckSection: React.FC = () => {
|
||||||
|
const [spellCheckEnabled, setSpellCheckEnabled] = useState<boolean | undefined>();
|
||||||
|
const [spellCheckLanguages, setSpellCheckLanguages] = useState<string[] | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const plaf = PlatformPeg.get();
|
||||||
|
const [enabled, langs] = await Promise.all([plaf?.getSpellCheckEnabled(), plaf?.getSpellCheckLanguages()]);
|
||||||
|
|
||||||
|
setSpellCheckEnabled(enabled);
|
||||||
|
setSpellCheckLanguages(langs || undefined);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onSpellCheckEnabledChange = useCallback((enabled: boolean) => {
|
||||||
|
setSpellCheckEnabled(enabled);
|
||||||
|
PlatformPeg.get()?.setSpellCheckEnabled(enabled);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onSpellCheckLanguagesChange = useCallback((languages: string[]): void => {
|
||||||
|
setSpellCheckLanguages(languages);
|
||||||
|
PlatformPeg.get()?.setSpellCheckLanguages(languages);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!PlatformPeg.get()?.supportsSpellCheckSettings()) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LabelledToggleSwitch
|
||||||
|
label={_t("settings|general|allow_spellcheck")}
|
||||||
|
value={Boolean(spellCheckEnabled)}
|
||||||
|
onChange={onSpellCheckEnabledChange}
|
||||||
|
/>
|
||||||
|
{spellCheckEnabled && spellCheckLanguages !== undefined && !IS_MAC && (
|
||||||
|
<SpellCheckSettings languages={spellCheckLanguages} onLanguagesChange={onSpellCheckLanguagesChange} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default class PreferencesUserSettingsTab extends React.Component<IProps, IState> {
|
export default class PreferencesUserSettingsTab extends React.Component<IProps, IState> {
|
||||||
private static ROOM_LIST_SETTINGS = ["breadcrumbs", "FTUE.userOnboardingButton"];
|
private static ROOM_LIST_SETTINGS = ["breadcrumbs", "FTUE.userOnboardingButton"];
|
||||||
|
|
||||||
|
@ -146,6 +224,12 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||||
return (
|
return (
|
||||||
<SettingsTab data-testid="mx_PreferencesUserSettingsTab">
|
<SettingsTab data-testid="mx_PreferencesUserSettingsTab">
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
|
{/* The heading string is still 'general' from where it was moved, but this section should become 'general' */}
|
||||||
|
<SettingsSubsection heading={_t("settings|general|language_section")}>
|
||||||
|
<LanguageSection />
|
||||||
|
<SpellCheckSection />
|
||||||
|
</SettingsSubsection>
|
||||||
|
|
||||||
{roomListSettings.length > 0 && (
|
{roomListSettings.length > 0 && (
|
||||||
<SettingsSubsection heading={_t("settings|preferences|room_list_heading")}>
|
<SettingsSubsection heading={_t("settings|preferences|room_list_heading")}>
|
||||||
{this.renderGroup(roomListSettings)}
|
{this.renderGroup(roomListSettings)}
|
||||||
|
|
|
@ -2463,6 +2463,9 @@
|
||||||
"add_msisdn_dialog_title": "Add Phone Number",
|
"add_msisdn_dialog_title": "Add Phone Number",
|
||||||
"add_msisdn_instructions": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.",
|
"add_msisdn_instructions": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.",
|
||||||
"add_msisdn_misconfigured": "The add / bind with MSISDN flow is misconfigured",
|
"add_msisdn_misconfigured": "The add / bind with MSISDN flow is misconfigured",
|
||||||
|
"allow_spellcheck": "Allow spell check",
|
||||||
|
"application_language": "Application language",
|
||||||
|
"application_language_reload_hint": "The app will reload after selecting another language",
|
||||||
"avatar_remove_progress": "Removing image...",
|
"avatar_remove_progress": "Removing image...",
|
||||||
"avatar_save_progress": "Uploading image...",
|
"avatar_save_progress": "Uploading image...",
|
||||||
"avatar_upload_error_text": "The file format is not supported or the image is larger than %(size)s.",
|
"avatar_upload_error_text": "The file format is not supported or the image is larger than %(size)s.",
|
||||||
|
@ -2516,7 +2519,7 @@
|
||||||
"identity_server_no_token": "No identity access token found",
|
"identity_server_no_token": "No identity access token found",
|
||||||
"identity_server_not_set": "Identity server not set",
|
"identity_server_not_set": "Identity server not set",
|
||||||
"incorrect_msisdn_verification": "Incorrect verification code",
|
"incorrect_msisdn_verification": "Incorrect verification code",
|
||||||
"language_section": "Language and region",
|
"language_section": "Language",
|
||||||
"msisdn_in_use": "This phone number is already in use",
|
"msisdn_in_use": "This phone number is already in use",
|
||||||
"msisdn_label": "Phone Number",
|
"msisdn_label": "Phone Number",
|
||||||
"msisdn_verification_field_label": "Verification code",
|
"msisdn_verification_field_label": "Verification code",
|
||||||
|
@ -2532,7 +2535,6 @@
|
||||||
"remove_email_prompt": "Remove %(email)s?",
|
"remove_email_prompt": "Remove %(email)s?",
|
||||||
"remove_msisdn_prompt": "Remove %(phone)s?",
|
"remove_msisdn_prompt": "Remove %(phone)s?",
|
||||||
"spell_check_locale_placeholder": "Choose a locale",
|
"spell_check_locale_placeholder": "Choose a locale",
|
||||||
"spell_check_section": "Spell check",
|
|
||||||
"unable_to_load_emails": "Unable to load email addresses",
|
"unable_to_load_emails": "Unable to load email addresses",
|
||||||
"unable_to_load_msisdns": "Unable to load phone numbers",
|
"unable_to_load_msisdns": "Unable to load phone numbers",
|
||||||
"username": "Username"
|
"username": "Username"
|
||||||
|
|
|
@ -15,7 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render, RenderResult, waitFor } from "@testing-library/react";
|
import { fireEvent, render, RenderResult, screen, waitFor } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import PreferencesUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/PreferencesUserSettingsTab";
|
import PreferencesUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/PreferencesUserSettingsTab";
|
||||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||||
|
@ -23,6 +24,7 @@ import { mockPlatformPeg, stubClient } from "../../../../../test-utils";
|
||||||
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
||||||
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
|
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
|
||||||
import MatrixClientBackedController from "../../../../../../src/settings/controllers/MatrixClientBackedController";
|
import MatrixClientBackedController from "../../../../../../src/settings/controllers/MatrixClientBackedController";
|
||||||
|
import PlatformPeg from "../../../../../../src/PlatformPeg";
|
||||||
|
|
||||||
describe("PreferencesUserSettingsTab", () => {
|
describe("PreferencesUserSettingsTab", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -38,6 +40,43 @@ describe("PreferencesUserSettingsTab", () => {
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should reload when changing language", async () => {
|
||||||
|
const reloadStub = jest.fn();
|
||||||
|
PlatformPeg.get()!.reload = reloadStub;
|
||||||
|
|
||||||
|
renderTab();
|
||||||
|
const languageDropdown = await screen.findByRole("button", { name: "Language Dropdown" });
|
||||||
|
expect(languageDropdown).toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.click(languageDropdown);
|
||||||
|
|
||||||
|
const germanOption = await screen.findByText("Deutsch");
|
||||||
|
await userEvent.click(germanOption);
|
||||||
|
expect(reloadStub).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not show spell check setting if unsupported", async () => {
|
||||||
|
PlatformPeg.get()!.supportsSpellCheckSettings = jest.fn().mockReturnValue(false);
|
||||||
|
|
||||||
|
renderTab();
|
||||||
|
expect(screen.queryByRole("switch", { name: "Allow spell check" })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enable spell check", async () => {
|
||||||
|
const spellCheckEnableFn = jest.fn();
|
||||||
|
PlatformPeg.get()!.supportsSpellCheckSettings = jest.fn().mockReturnValue(true);
|
||||||
|
PlatformPeg.get()!.getSpellCheckEnabled = jest.fn().mockReturnValue(false);
|
||||||
|
PlatformPeg.get()!.setSpellCheckEnabled = spellCheckEnableFn;
|
||||||
|
|
||||||
|
renderTab();
|
||||||
|
const toggle = await screen.findByRole("switch", { name: "Allow spell check" });
|
||||||
|
expect(toggle).toHaveAttribute("aria-checked", "false");
|
||||||
|
|
||||||
|
await userEvent.click(toggle);
|
||||||
|
|
||||||
|
expect(spellCheckEnableFn).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
describe("send read receipts", () => {
|
describe("send read receipts", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stubClient();
|
stubClient();
|
||||||
|
|
|
@ -15,6 +15,44 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSection_subSections"
|
class="mx_SettingsSection_subSections"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsSubsection"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsSubsectionHeading"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
|
>
|
||||||
|
Language
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsSubsection_content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsSubsection_contentStretch"
|
||||||
|
>
|
||||||
|
Application language
|
||||||
|
<div
|
||||||
|
class="mx_Spinner"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Loading…"
|
||||||
|
class="mx_Spinner_icon"
|
||||||
|
data-testid="spinner"
|
||||||
|
role="progressbar"
|
||||||
|
style="width: 32px; height: 32px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_GeneralUserSettingsTab_section_hint"
|
||||||
|
>
|
||||||
|
The app will reload after selecting another language
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection"
|
class="mx_SettingsSubsection"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue