Use browser's font size instead of hardcoded 16px
as root font size (#12246)
* WIP Use browser font size instead of hardcoded 16px * Add font migration to v3 * Remove custom font size input * Use a dropdown instead of a slider * Add margin to the font size dropdown * Fix `UpdateFontSizeDelta` action typo * Fix `fontScale`in `Call.ts` * Rename `baseFontSizeV3` to `fontSizeDelta` * Update playwright test * Add `default` next to the browser font size * Remove remaining `TODO` * Remove falsy `private` * Improve doc * Update snapshots after develop merge * Remove commented import
This commit is contained in:
parent
36a8d503df
commit
6d55ce0217
17 changed files with 453 additions and 366 deletions
|
@ -73,48 +73,18 @@ test.describe("Appearance user settings tab", () => {
|
||||||
await expect(page.locator(".mx_RoomView_body[data-layout='bubble']")).toBeVisible();
|
await expect(page.locator(".mx_RoomView_body[data-layout='bubble']")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should support changing font size by clicking the font slider", async ({ page, app, user }) => {
|
test("should support changing font size by using the font size dropdown", async ({ page, app, user }) => {
|
||||||
await app.settings.openUserSettings("Appearance");
|
await app.settings.openUserSettings("Appearance");
|
||||||
|
|
||||||
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
|
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
|
||||||
const fontSliderSection = tab.locator(".mx_FontScalingPanel_fontSlider");
|
const fontDropdown = tab.locator(".mx_FontScalingPanel_Dropdown");
|
||||||
|
await expect(fontDropdown.getByLabel("Font size")).toBeVisible();
|
||||||
|
|
||||||
await expect(fontSliderSection.getByLabel("Font size")).toBeVisible();
|
// Default browser font size is 16px and the select value is 0
|
||||||
|
// -4 value is 12px
|
||||||
|
await fontDropdown.getByLabel("Font size").selectOption({ value: "-4" });
|
||||||
|
|
||||||
const slider = fontSliderSection.getByRole("slider");
|
await expect(page).toMatchScreenshot("window-12px.png");
|
||||||
// Click the left position of the slider
|
|
||||||
await slider.click({ position: { x: 0, y: 10 } });
|
|
||||||
|
|
||||||
const MIN_FONT_SIZE = 11;
|
|
||||||
// Assert that the smallest font size is selected
|
|
||||||
await expect(fontSliderSection.locator(`input[value='${MIN_FONT_SIZE}']`)).toBeVisible();
|
|
||||||
await expect(
|
|
||||||
fontSliderSection.locator("output .mx_Slider_selection_label", { hasText: String(MIN_FONT_SIZE) }),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
await expect(fontSliderSection).toMatchScreenshot(`font-slider-${MIN_FONT_SIZE}.png`);
|
|
||||||
|
|
||||||
// Click the right position of the slider
|
|
||||||
await slider.click({ position: { x: 572, y: 10 } });
|
|
||||||
|
|
||||||
const MAX_FONT_SIZE = 21;
|
|
||||||
// Assert that the largest font size is selected
|
|
||||||
await expect(fontSliderSection.locator(`input[value='${MAX_FONT_SIZE}']`)).toBeVisible();
|
|
||||||
await expect(
|
|
||||||
fontSliderSection.locator("output .mx_Slider_selection_label", { hasText: String(MAX_FONT_SIZE) }),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
await expect(fontSliderSection).toMatchScreenshot(`font-slider-${MAX_FONT_SIZE}.png`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should disable font size slider when custom font size is used", async ({ page, app, user }) => {
|
|
||||||
await app.settings.openUserSettings("Appearance");
|
|
||||||
|
|
||||||
const panel = page.getByTestId("mx_FontScalingPanel");
|
|
||||||
await panel.locator("label", { hasText: "Use custom size" }).click();
|
|
||||||
|
|
||||||
// Assert that the font slider is disabled
|
|
||||||
await expect(panel.locator(".mx_FontScalingPanel_fontSlider input[disabled]")).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should support enabling compact group (modern) layout", async ({ page, app, user }) => {
|
test("should support enabling compact group (modern) layout", async ({ page, app, user }) => {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
|
@ -36,26 +36,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_FontScalingPanel_fontSlider {
|
.mx_FontScalingPanel_Dropdown {
|
||||||
display: flex;
|
width: 120px;
|
||||||
align-items: center;
|
/* Override default mx_Field margin */
|
||||||
padding: 15px $spacing-20 35px;
|
margin-bottom: var(--cpd-space-2x) !important;
|
||||||
background: $panels;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: $font-10px;
|
|
||||||
|
|
||||||
.mx_FontScalingPanel_fontSlider_smallText,
|
|
||||||
.mx_FontScalingPanel_fontSlider_largeText {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_FontScalingPanel_fontSlider_smallText {
|
|
||||||
font-size: $font-15px;
|
|
||||||
padding-inline-end: $spacing-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_FontScalingPanel_fontSlider_largeText {
|
|
||||||
font-size: $font-18px;
|
|
||||||
padding-inline-start: $spacing-20;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,29 +14,26 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent } from "react";
|
import React from "react";
|
||||||
|
|
||||||
import EventTilePreview from "../elements/EventTilePreview";
|
import EventTilePreview from "../elements/EventTilePreview";
|
||||||
import Field from "../elements/Field";
|
|
||||||
import SettingsFlag from "../elements/SettingsFlag";
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import Slider from "../elements/Slider";
|
|
||||||
import { FontWatcher } from "../../../settings/watchers/FontWatcher";
|
|
||||||
import { IValidationResult, IFieldState } from "../elements/Validation";
|
|
||||||
import { Layout } from "../../../settings/enums/Layout";
|
import { Layout } from "../../../settings/enums/Layout";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { clamp } from "../../../utils/numbers";
|
|
||||||
import SettingsSubsection from "./shared/SettingsSubsection";
|
import SettingsSubsection from "./shared/SettingsSubsection";
|
||||||
|
import Field from "../elements/Field";
|
||||||
|
import { FontWatcher } from "../../../settings/watchers/FontWatcher";
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
browserFontSize: number;
|
||||||
// String displaying the current selected fontSize.
|
// String displaying the current selected fontSize.
|
||||||
// Needs to be string for things like '17.' without
|
// Needs to be string for things like '1.' without
|
||||||
// trailing 0s.
|
// trailing 0s.
|
||||||
fontSize: string;
|
fontSizeDelta: number;
|
||||||
useCustomFontSize: boolean;
|
useCustomFontSize: boolean;
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
// User profile data for the message preview
|
// User profile data for the message preview
|
||||||
|
@ -47,6 +44,10 @@ interface IState {
|
||||||
|
|
||||||
export default class FontScalingPanel extends React.Component<IProps, IState> {
|
export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
private readonly MESSAGE_PREVIEW_TEXT = _t("common|preview_message");
|
private readonly MESSAGE_PREVIEW_TEXT = _t("common|preview_message");
|
||||||
|
/**
|
||||||
|
* Font sizes available (in px)
|
||||||
|
*/
|
||||||
|
private readonly sizes = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36];
|
||||||
private layoutWatcherRef?: string;
|
private layoutWatcherRef?: string;
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
|
@ -54,7 +55,8 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
fontSize: SettingsStore.getValue("baseFontSizeV2", null).toString(),
|
fontSizeDelta: SettingsStore.getValue<number>("fontSizeDelta", null),
|
||||||
|
browserFontSize: FontWatcher.getBrowserDefaultFontSize(),
|
||||||
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
|
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
|
||||||
layout: SettingsStore.getValue("layout"),
|
layout: SettingsStore.getValue("layout"),
|
||||||
};
|
};
|
||||||
|
@ -90,30 +92,22 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onFontSizeChanged = (size: number): void => {
|
/**
|
||||||
this.setState({ fontSize: size.toString() });
|
* Save the new font size
|
||||||
SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, size);
|
* @param delta
|
||||||
|
*/
|
||||||
|
private onFontSizeChanged = async (delta: string): Promise<void> => {
|
||||||
|
const parsedDelta = parseInt(delta, 10) || 0;
|
||||||
|
this.setState({ fontSizeDelta: parsedDelta });
|
||||||
|
await SettingsStore.setValue("fontSizeDelta", null, SettingLevel.DEVICE, parsedDelta);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onValidateFontSize = async ({ value }: Pick<IFieldState, "value">): Promise<IValidationResult> => {
|
/**
|
||||||
const parsedSize = parseFloat(value!);
|
* Compute the difference between the selected font size and the browser font size
|
||||||
const min = FontWatcher.MIN_SIZE;
|
* @param fontSize
|
||||||
const max = FontWatcher.MAX_SIZE;
|
*/
|
||||||
|
private computeDeltaFontSize = (fontSize: number): number => {
|
||||||
if (isNaN(parsedSize)) {
|
return fontSize - this.state.browserFontSize;
|
||||||
return { valid: false, feedback: _t("settings|appearance|font_size_nan") };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(min <= parsedSize && parsedSize <= max)) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
feedback: _t("settings|appearance|font_size_limit", { min, max }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, parseInt(value!, 10));
|
|
||||||
|
|
||||||
return { valid: true, feedback: _t("settings|appearance|font_size_valid", { min, max }) };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
@ -123,6 +117,21 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
stretchContent
|
stretchContent
|
||||||
data-testid="mx_FontScalingPanel"
|
data-testid="mx_FontScalingPanel"
|
||||||
>
|
>
|
||||||
|
<Field
|
||||||
|
element="select"
|
||||||
|
className="mx_FontScalingPanel_Dropdown"
|
||||||
|
label={_t("settings|appearance|font_size")}
|
||||||
|
value={this.state.fontSizeDelta.toString()}
|
||||||
|
onChange={(e) => this.onFontSizeChanged(e.target.value)}
|
||||||
|
>
|
||||||
|
{this.sizes.map((size) => (
|
||||||
|
<option key={size} value={this.computeDeltaFontSize(size)}>
|
||||||
|
{size === this.state.browserFontSize
|
||||||
|
? _t("settings|appearance|font_size_default", { fontSize: size })
|
||||||
|
: size}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
<EventTilePreview
|
<EventTilePreview
|
||||||
className="mx_FontScalingPanel_preview"
|
className="mx_FontScalingPanel_preview"
|
||||||
message={this.MESSAGE_PREVIEW_TEXT}
|
message={this.MESSAGE_PREVIEW_TEXT}
|
||||||
|
@ -131,49 +140,6 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
displayName={this.state.displayName}
|
displayName={this.state.displayName}
|
||||||
avatarUrl={this.state.avatarUrl}
|
avatarUrl={this.state.avatarUrl}
|
||||||
/>
|
/>
|
||||||
<div className="mx_FontScalingPanel_fontSlider">
|
|
||||||
<div className="mx_FontScalingPanel_fontSlider_smallText">Aa</div>
|
|
||||||
<Slider
|
|
||||||
min={FontWatcher.MIN_SIZE}
|
|
||||||
max={FontWatcher.MAX_SIZE}
|
|
||||||
step={1}
|
|
||||||
value={parseInt(this.state.fontSize, 10)}
|
|
||||||
onChange={this.onFontSizeChanged}
|
|
||||||
displayFunc={(_) => ""}
|
|
||||||
disabled={this.state.useCustomFontSize}
|
|
||||||
label={_t("settings|appearance|font_size")}
|
|
||||||
/>
|
|
||||||
<div className="mx_FontScalingPanel_fontSlider_largeText">Aa</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SettingsFlag
|
|
||||||
name="useCustomFontSize"
|
|
||||||
level={SettingLevel.ACCOUNT}
|
|
||||||
onChange={(checked) => {
|
|
||||||
this.setState({ useCustomFontSize: checked });
|
|
||||||
if (!checked) {
|
|
||||||
const size = parseInt(this.state.fontSize, 10);
|
|
||||||
const clamped = clamp(size, FontWatcher.MIN_SIZE, FontWatcher.MAX_SIZE);
|
|
||||||
if (clamped !== size) {
|
|
||||||
this.onFontSizeChanged(clamped);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
useCheckbox={true}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Field
|
|
||||||
type="number"
|
|
||||||
label={_t("settings|appearance|font_size")}
|
|
||||||
autoComplete="off"
|
|
||||||
placeholder={this.state.fontSize.toString()}
|
|
||||||
value={this.state.fontSize.toString()}
|
|
||||||
id="font_size_field"
|
|
||||||
onValidate={this.onValidateFontSize}
|
|
||||||
onChange={(value: ChangeEvent<HTMLInputElement>) => this.setState({ fontSize: value.target.value })}
|
|
||||||
disabled={!this.state.useCustomFontSize}
|
|
||||||
className="mx_AppearanceUserSettingsTab_checkboxControlledField"
|
|
||||||
/>
|
|
||||||
</SettingsSubsection>
|
</SettingsSubsection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,9 +107,11 @@ export enum Action {
|
||||||
MigrateBaseFontSize = "migrate_base_font_size",
|
MigrateBaseFontSize = "migrate_base_font_size",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the apps root font size. Should be used with UpdateFontSizePayload
|
* Sets the apps root font size delta. Should be used with UpdateFontSizeDeltaPayload
|
||||||
|
* It will add the delta to the current font size.
|
||||||
|
* The delta should be between {@link FontWatcher.MIN_DELTA} and {@link FontWatcher.MAX_DELTA}.
|
||||||
*/
|
*/
|
||||||
UpdateFontSize = "update_font_size",
|
UpdateFontSizeDelta = "update_font_size_delta",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a system font. Should be used with UpdateSystemFontPayload
|
* Sets a system font. Should be used with UpdateSystemFontPayload
|
||||||
|
|
|
@ -17,11 +17,12 @@ limitations under the License.
|
||||||
import { ActionPayload } from "../payloads";
|
import { ActionPayload } from "../payloads";
|
||||||
import { Action } from "../actions";
|
import { Action } from "../actions";
|
||||||
|
|
||||||
export interface UpdateFontSizePayload extends ActionPayload {
|
export interface UpdateFontSizeDeltaPayload extends ActionPayload {
|
||||||
action: Action.UpdateFontSize;
|
action: Action.UpdateFontSizeDelta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font size to set the root to
|
* The delta is added to the current font size.
|
||||||
|
* The delta should be between {@link FontWatcher.MIN_DELTA} and {@link FontWatcher.MAX_DELTA}.
|
||||||
*/
|
*/
|
||||||
size: number;
|
delta: number;
|
||||||
}
|
}
|
|
@ -2422,9 +2422,7 @@
|
||||||
"custom_theme_success": "Theme added!",
|
"custom_theme_success": "Theme added!",
|
||||||
"custom_theme_url": "Custom theme URL",
|
"custom_theme_url": "Custom theme URL",
|
||||||
"font_size": "Font size",
|
"font_size": "Font size",
|
||||||
"font_size_limit": "Custom font size can only be between %(min)s pt and %(max)s pt",
|
"font_size_default": "%(fontSize)s (default)",
|
||||||
"font_size_nan": "Size must be a number",
|
|
||||||
"font_size_valid": "Use between %(min)s pt and %(max)s pt",
|
|
||||||
"heading": "Customise your appearance",
|
"heading": "Customise your appearance",
|
||||||
"image_size_default": "Default",
|
"image_size_default": "Default",
|
||||||
"image_size_large": "Large",
|
"image_size_large": "Large",
|
||||||
|
|
|
@ -52,11 +52,11 @@ import WidgetStore from "../stores/WidgetStore";
|
||||||
import { WidgetMessagingStore, WidgetMessagingStoreEvent } from "../stores/widgets/WidgetMessagingStore";
|
import { WidgetMessagingStore, WidgetMessagingStoreEvent } from "../stores/widgets/WidgetMessagingStore";
|
||||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../stores/ActiveWidgetStore";
|
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../stores/ActiveWidgetStore";
|
||||||
import { getCurrentLanguage } from "../languageHandler";
|
import { getCurrentLanguage } from "../languageHandler";
|
||||||
import { FontWatcher } from "../settings/watchers/FontWatcher";
|
|
||||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||||
import { UPDATE_EVENT } from "../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../stores/AsyncStore";
|
||||||
import { getJoinedNonFunctionalMembers } from "../utils/room/getJoinedNonFunctionalMembers";
|
import { getJoinedNonFunctionalMembers } from "../utils/room/getJoinedNonFunctionalMembers";
|
||||||
import { isVideoRoom } from "../utils/video-rooms";
|
import { isVideoRoom } from "../utils/video-rooms";
|
||||||
|
import { FontWatcher } from "../settings/watchers/FontWatcher";
|
||||||
|
|
||||||
const TIMEOUT_MS = 16000;
|
const TIMEOUT_MS = 16000;
|
||||||
|
|
||||||
|
@ -687,7 +687,8 @@ export class ElementCall extends Call {
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
baseUrl: client.baseUrl,
|
baseUrl: client.baseUrl,
|
||||||
lang: getCurrentLanguage().replace("_", "-"),
|
lang: getCurrentLanguage().replace("_", "-"),
|
||||||
fontScale: `${(SettingsStore.getValue("baseFontSizeV2") ?? 16) / FontWatcher.DEFAULT_SIZE}`,
|
fontScale: (FontWatcher.getRootFontSize() / FontWatcher.getBrowserDefaultFontSize()).toString(),
|
||||||
|
|
||||||
analyticsID,
|
analyticsID,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -511,6 +511,9 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
||||||
supportedLevels: [SettingLevel.CONFIG],
|
supportedLevels: [SettingLevel.CONFIG],
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @deprecated in favor of {@link fontSizeDelta}
|
||||||
|
*/
|
||||||
"baseFontSize": {
|
"baseFontSize": {
|
||||||
displayName: _td("settings|appearance|font_size"),
|
displayName: _td("settings|appearance|font_size"),
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
|
@ -530,12 +533,22 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
||||||
* With the transition to Compound we are moving to a base font size
|
* With the transition to Compound we are moving to a base font size
|
||||||
* of 16px. We're taking the opportunity to move away from the `baseFontSize`
|
* of 16px. We're taking the opportunity to move away from the `baseFontSize`
|
||||||
* setting that had a 5px offset.
|
* setting that had a 5px offset.
|
||||||
*
|
* @deprecated in favor {@link fontSizeDelta}
|
||||||
*/
|
*/
|
||||||
"baseFontSizeV2": {
|
"baseFontSizeV2": {
|
||||||
displayName: _td("settings|appearance|font_size"),
|
displayName: _td("settings|appearance|font_size"),
|
||||||
supportedLevels: [SettingLevel.DEVICE],
|
supportedLevels: [SettingLevel.DEVICE],
|
||||||
default: FontWatcher.DEFAULT_SIZE,
|
default: "",
|
||||||
|
controller: new FontSizeController(),
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This delta is added to the browser default font size
|
||||||
|
* Moving from `baseFontSizeV2` to `fontSizeDelta` to replace the default 16px to --cpd-font-size-root (browser default font size) + fontSizeDelta
|
||||||
|
*/
|
||||||
|
"fontSizeDelta": {
|
||||||
|
displayName: _td("settings|appearance|font_size"),
|
||||||
|
supportedLevels: [SettingLevel.DEVICE],
|
||||||
|
default: FontWatcher.DEFAULT_DELTA,
|
||||||
controller: new FontSizeController(),
|
controller: new FontSizeController(),
|
||||||
},
|
},
|
||||||
"useCustomFontSize": {
|
"useCustomFontSize": {
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import SettingController from "./SettingController";
|
import SettingController from "./SettingController";
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
import { UpdateFontSizePayload } from "../../dispatcher/payloads/UpdateFontSizePayload";
|
import { UpdateFontSizeDeltaPayload } from "../../dispatcher/payloads/UpdateFontSizeDeltaPayload";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import { SettingLevel } from "../SettingLevel";
|
import { SettingLevel } from "../SettingLevel";
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ export default class FontSizeController extends SettingController {
|
||||||
dis.fire(Action.MigrateBaseFontSize);
|
dis.fire(Action.MigrateBaseFontSize);
|
||||||
} else if (newValue !== "") {
|
} else if (newValue !== "") {
|
||||||
// Dispatch font size change so that everything open responds to the change.
|
// Dispatch font size change so that everything open responds to the change.
|
||||||
dis.dispatch<UpdateFontSizePayload>({
|
dis.dispatch<UpdateFontSizeDeltaPayload>({
|
||||||
action: Action.UpdateFontSize,
|
action: Action.UpdateFontSizeDelta,
|
||||||
size: newValue,
|
delta: newValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,19 @@ import { Action } from "../../dispatcher/actions";
|
||||||
import { SettingLevel } from "../SettingLevel";
|
import { SettingLevel } from "../SettingLevel";
|
||||||
import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload";
|
import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload";
|
||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { clamp } from "../../utils/numbers";
|
|
||||||
|
|
||||||
export class FontWatcher implements IWatcher {
|
export class FontWatcher implements IWatcher {
|
||||||
/**
|
/**
|
||||||
* Value indirectly defined by Compound.
|
* This Compound value is using `100%` of the default browser font size.
|
||||||
* All `rem` calculations are made from a `16px` values in the
|
* It allows EW to use the browser's default font size instead of a fixed value.
|
||||||
* @vector-im/compound-design-tokens package
|
* All the Compound font size are using `rem`, they are relative to the root font size
|
||||||
*
|
* and therefore of the browser font size.
|
||||||
* We might want to move to using `100%` instead so we can inherit the user
|
|
||||||
* preference set in the browser regarding font sizes.
|
|
||||||
*/
|
*/
|
||||||
public static readonly DEFAULT_SIZE = 16;
|
private static readonly DEFAULT_SIZE = "var(--cpd-font-size-root)";
|
||||||
public static readonly MIN_SIZE = FontWatcher.DEFAULT_SIZE - 5;
|
/**
|
||||||
public static readonly MAX_SIZE = FontWatcher.DEFAULT_SIZE + 5;
|
* Default delta added to the ${@link DEFAULT_SIZE}
|
||||||
|
*/
|
||||||
|
public static readonly DEFAULT_DELTA = 0;
|
||||||
|
|
||||||
private dispatcherRef: string | null;
|
private dispatcherRef: string | null;
|
||||||
|
|
||||||
|
@ -54,28 +53,106 @@ export class FontWatcher implements IWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrating the old `baseFontSize` for Compound.
|
* Migrate the base font size from the V1 and V2 version to the V3 version
|
||||||
* Everything will becomes slightly larger, and getting rid of the `SIZE_DIFF`
|
* @private
|
||||||
* weirdness for locally persisted values
|
|
||||||
*/
|
*/
|
||||||
private async migrateBaseFontSize(): Promise<void> {
|
private async migrateBaseFontSize(): Promise<void> {
|
||||||
const legacyBaseFontSize = SettingsStore.getValue("baseFontSize");
|
await this.migrateBaseFontV1toFontSizeDelta();
|
||||||
if (legacyBaseFontSize) {
|
await this.migrateBaseFontV2toFontSizeDelta();
|
||||||
console.log("Migrating base font size for Compound, current value", legacyBaseFontSize);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrating from the V1 version of the base font size to the new delta system.
|
||||||
|
* The delta system is using the default browser font size as a base
|
||||||
|
* Everything will become slightly larger, and getting rid of the `SIZE_DIFF`
|
||||||
|
* weirdness for locally persisted values
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private async migrateBaseFontV1toFontSizeDelta(): Promise<void> {
|
||||||
|
const legacyBaseFontSize = SettingsStore.getValue<number>("baseFontSize");
|
||||||
|
// No baseFontV1 found, nothing to migrate
|
||||||
|
if (!legacyBaseFontSize) return;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Migrating base font size -> base font size V2 -> font size delta for Compound, current value",
|
||||||
|
legacyBaseFontSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute the V1 to V2 version before migrating to fontSizeDelta
|
||||||
|
const baseFontSizeV2 = this.computeBaseFontSizeV1toV2(legacyBaseFontSize);
|
||||||
|
|
||||||
|
// Compute the difference between the V2 and the fontSizeDelta
|
||||||
|
const delta = this.computeFontSizeDeltaFromV2BaseFontSize(baseFontSizeV2);
|
||||||
|
|
||||||
|
await SettingsStore.setValue("fontSizeDelta", null, SettingLevel.DEVICE, delta);
|
||||||
|
await SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, 0);
|
||||||
|
console.log("Migration complete, deleting legacy `baseFontSize`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrating from the V2 version of the base font size to the new delta system
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private async migrateBaseFontV2toFontSizeDelta(): Promise<void> {
|
||||||
|
const legacyBaseFontV2Size = SettingsStore.getValue<number>("baseFontSizeV2");
|
||||||
|
// No baseFontV2 found, nothing to migrate
|
||||||
|
if (!legacyBaseFontV2Size) return;
|
||||||
|
|
||||||
|
console.log("Migrating base font size V2 for Compound, current value", legacyBaseFontV2Size);
|
||||||
|
|
||||||
|
// Compute the difference between the V2 and the fontSizeDelta
|
||||||
|
const delta = this.computeFontSizeDeltaFromV2BaseFontSize(legacyBaseFontV2Size);
|
||||||
|
|
||||||
|
await SettingsStore.setValue("fontSizeDelta", null, SettingLevel.DEVICE, delta);
|
||||||
|
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, 0);
|
||||||
|
console.log("Migration complete, deleting legacy `baseFontSizeV2`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the V2 font size from the V1 font size
|
||||||
|
* @param legacyBaseFontSize
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private computeBaseFontSizeV1toV2(legacyBaseFontSize: number): number {
|
||||||
// For some odd reason, the persisted value in user storage has an offset
|
// For some odd reason, the persisted value in user storage has an offset
|
||||||
// of 5 pixels for all values stored under `baseFontSize`
|
// of 5 pixels for all values stored under `baseFontSize`
|
||||||
const LEGACY_SIZE_DIFF = 5;
|
const LEGACY_SIZE_DIFF = 5;
|
||||||
|
|
||||||
// Compound uses a base font size of `16px`, whereas the old Element
|
// Compound uses a base font size of `16px`, whereas the old Element
|
||||||
// styles based their calculations off a `15px` root font size.
|
// styles based their calculations off a `15px` root font size.
|
||||||
const ROOT_FONT_SIZE_INCREASE = 1;
|
const ROOT_FONT_SIZE_INCREASE = 1;
|
||||||
|
|
||||||
const baseFontSize = legacyBaseFontSize + ROOT_FONT_SIZE_INCREASE + LEGACY_SIZE_DIFF;
|
// Compute the font size of the V2 version before migrating to V3
|
||||||
|
return legacyBaseFontSize + ROOT_FONT_SIZE_INCREASE + LEGACY_SIZE_DIFF;
|
||||||
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, baseFontSize);
|
|
||||||
await SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, "");
|
|
||||||
console.log("Migration complete, deleting legacy `baseFontSize`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the difference between the V2 font size and the default browser font size
|
||||||
|
* @param legacyBaseFontV2Size
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private computeFontSizeDeltaFromV2BaseFontSize(legacyBaseFontV2Size: number): number {
|
||||||
|
const browserDefaultFontSize = FontWatcher.getRootFontSize();
|
||||||
|
|
||||||
|
// Compute the difference between the V2 font size and the default browser font size
|
||||||
|
return legacyBaseFontV2Size - browserDefaultFontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root font size of the document
|
||||||
|
* Fallback to 16px if the value is not found
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
public static getRootFontSize(): number {
|
||||||
|
return parseInt(window.getComputedStyle(document.documentElement).getPropertyValue("font-size"), 10) || 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the browser default font size
|
||||||
|
* @returns {number} the default font size of the browser
|
||||||
|
*/
|
||||||
|
public static getBrowserDefaultFontSize(): number {
|
||||||
|
return this.getRootFontSize() - SettingsStore.getValue<number>("fontSizeDelta");
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop(): void {
|
public stop(): void {
|
||||||
|
@ -84,7 +161,7 @@ export class FontWatcher implements IWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateFont(): void {
|
private updateFont(): void {
|
||||||
this.setRootFontSize(SettingsStore.getValue("baseFontSizeV2"));
|
this.setRootFontSize(SettingsStore.getValue<number>("fontSizeDelta"));
|
||||||
this.setSystemFont({
|
this.setSystemFont({
|
||||||
useBundledEmojiFont: SettingsStore.getValue("useBundledEmojiFont"),
|
useBundledEmojiFont: SettingsStore.getValue("useBundledEmojiFont"),
|
||||||
useSystemFont: SettingsStore.getValue("useSystemFont"),
|
useSystemFont: SettingsStore.getValue("useSystemFont"),
|
||||||
|
@ -95,13 +172,13 @@ export class FontWatcher implements IWatcher {
|
||||||
private onAction = (payload: ActionPayload): void => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.action === Action.MigrateBaseFontSize) {
|
if (payload.action === Action.MigrateBaseFontSize) {
|
||||||
this.migrateBaseFontSize();
|
this.migrateBaseFontSize();
|
||||||
} else if (payload.action === Action.UpdateFontSize) {
|
} else if (payload.action === Action.UpdateFontSizeDelta) {
|
||||||
this.setRootFontSize(payload.size);
|
this.setRootFontSize(payload.delta);
|
||||||
} else if (payload.action === Action.UpdateSystemFont) {
|
} else if (payload.action === Action.UpdateSystemFont) {
|
||||||
this.setSystemFont(payload as UpdateSystemFontPayload);
|
this.setSystemFont(payload as UpdateSystemFontPayload);
|
||||||
} else if (payload.action === Action.OnLoggedOut) {
|
} else if (payload.action === Action.OnLoggedOut) {
|
||||||
// Clear font overrides when logging out
|
// Clear font overrides when logging out
|
||||||
this.setRootFontSize(FontWatcher.DEFAULT_SIZE);
|
this.setRootFontSize(FontWatcher.DEFAULT_DELTA);
|
||||||
this.setSystemFont({
|
this.setSystemFont({
|
||||||
useBundledEmojiFont: false,
|
useBundledEmojiFont: false,
|
||||||
useSystemFont: false,
|
useSystemFont: false,
|
||||||
|
@ -113,13 +190,14 @@ export class FontWatcher implements IWatcher {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private setRootFontSize = async (size: number): Promise<void> => {
|
/**
|
||||||
const fontSize = clamp(size, FontWatcher.MIN_SIZE, FontWatcher.MAX_SIZE);
|
* Set the root font size of the document
|
||||||
|
* @param delta {number} the delta to add to the default font size
|
||||||
if (fontSize !== size) {
|
*/
|
||||||
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, fontSize);
|
private setRootFontSize = async (delta: number): Promise<void> => {
|
||||||
}
|
// Add the delta to the browser default font size
|
||||||
document.querySelector<HTMLElement>(":root")!.style.fontSize = toPx(fontSize);
|
document.querySelector<HTMLElement>(":root")!.style.fontSize =
|
||||||
|
`calc(${FontWatcher.DEFAULT_SIZE} + ${toPx(delta)})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly FONT_FAMILY_CUSTOM_PROPERTY = "--cpd-font-family-sans";
|
public static readonly FONT_FAMILY_CUSTOM_PROPERTY = "--cpd-font-family-sans";
|
||||||
|
|
|
@ -15,11 +15,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render, waitFor } from "@testing-library/react";
|
import { render } from "@testing-library/react";
|
||||||
|
|
||||||
import * as TestUtils from "../../../test-utils";
|
import * as TestUtils from "../../../test-utils";
|
||||||
import FontScalingPanel from "../../../../src/components/views/settings/FontScalingPanel";
|
import FontScalingPanel from "../../../../src/components/views/settings/FontScalingPanel";
|
||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
|
||||||
|
|
||||||
describe("FontScalingPanel", () => {
|
describe("FontScalingPanel", () => {
|
||||||
it("renders the font scaling UI", () => {
|
it("renders the font scaling UI", () => {
|
||||||
|
@ -27,19 +26,4 @@ describe("FontScalingPanel", () => {
|
||||||
const { asFragment } = render(<FontScalingPanel />);
|
const { asFragment } = render(<FontScalingPanel />);
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should clamp custom font size when disabling it", async () => {
|
|
||||||
jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
|
|
||||||
TestUtils.stubClient();
|
|
||||||
const { container, getByText } = render(<FontScalingPanel />);
|
|
||||||
fireEvent.click(getByText("Use custom size"));
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector("input[checked]")).toBeDefined();
|
|
||||||
});
|
|
||||||
fireEvent.change(container.querySelector("#font_size_field")!, { target: { value: "25" } });
|
|
||||||
fireEvent.click(getByText("Use custom size"));
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector("#font_size_field")).toHaveValue(21);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,117 @@ exports[`FontScalingPanel renders the font scaling UI 1`] = `
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_contentStretch"
|
class="mx_SettingsSubsection_content mx_SettingsSubsection_contentStretch"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Field mx_Field_select mx_FontScalingPanel_Dropdown"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="mx_Field_1"
|
||||||
|
label="Font size"
|
||||||
|
placeholder="Font size"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="-7"
|
||||||
|
>
|
||||||
|
9
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-6"
|
||||||
|
>
|
||||||
|
10
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-5"
|
||||||
|
>
|
||||||
|
11
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-4"
|
||||||
|
>
|
||||||
|
12
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-3"
|
||||||
|
>
|
||||||
|
13
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-2"
|
||||||
|
>
|
||||||
|
14
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-1"
|
||||||
|
>
|
||||||
|
15
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="0"
|
||||||
|
>
|
||||||
|
16 (default)
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="1"
|
||||||
|
>
|
||||||
|
17
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="2"
|
||||||
|
>
|
||||||
|
18
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="4"
|
||||||
|
>
|
||||||
|
20
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="6"
|
||||||
|
>
|
||||||
|
22
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="8"
|
||||||
|
>
|
||||||
|
24
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="10"
|
||||||
|
>
|
||||||
|
26
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="12"
|
||||||
|
>
|
||||||
|
28
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="14"
|
||||||
|
>
|
||||||
|
30
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="16"
|
||||||
|
>
|
||||||
|
32
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="18"
|
||||||
|
>
|
||||||
|
34
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="20"
|
||||||
|
>
|
||||||
|
36
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<label
|
||||||
|
for="mx_Field_1"
|
||||||
|
>
|
||||||
|
Font size
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_FontScalingPanel_preview mx_EventTilePreview_loader"
|
class="mx_FontScalingPanel_preview mx_EventTilePreview_loader"
|
||||||
>
|
>
|
||||||
|
@ -33,83 +144,6 @@ exports[`FontScalingPanel renders the font scaling UI 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider_smallText"
|
|
||||||
>
|
|
||||||
Aa
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_Slider"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
aria-label="Font size"
|
|
||||||
autocomplete="off"
|
|
||||||
max="21"
|
|
||||||
min="11"
|
|
||||||
step="1"
|
|
||||||
type="range"
|
|
||||||
value="16"
|
|
||||||
/>
|
|
||||||
<output
|
|
||||||
class="mx_Slider_selection"
|
|
||||||
style="left: calc(2px + 50% + 1.2em - 1.2em);"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="mx_Slider_selection_label"
|
|
||||||
>
|
|
||||||
16
|
|
||||||
</span>
|
|
||||||
</output>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider_largeText"
|
|
||||||
>
|
|
||||||
Aa
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id="checkbox_QgU2PomxwK"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
for="checkbox_QgU2PomxwK"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Checkbox_background"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Checkbox_checkmark"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Use custom size
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="mx_Field mx_Field_input mx_AppearanceUserSettingsTab_checkboxControlledField"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
autocomplete="off"
|
|
||||||
disabled=""
|
|
||||||
id="font_size_field"
|
|
||||||
label="Font size"
|
|
||||||
placeholder="16"
|
|
||||||
type="number"
|
|
||||||
value="16"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
for="font_size_field"
|
|
||||||
>
|
|
||||||
Font size
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
|
|
|
@ -247,6 +247,117 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_contentStretch"
|
class="mx_SettingsSubsection_content mx_SettingsSubsection_contentStretch"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Field mx_Field_select mx_FontScalingPanel_Dropdown"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="mx_Field_1"
|
||||||
|
label="Font size"
|
||||||
|
placeholder="Font size"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="-7"
|
||||||
|
>
|
||||||
|
9
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-6"
|
||||||
|
>
|
||||||
|
10
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-5"
|
||||||
|
>
|
||||||
|
11
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-4"
|
||||||
|
>
|
||||||
|
12
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-3"
|
||||||
|
>
|
||||||
|
13
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-2"
|
||||||
|
>
|
||||||
|
14
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="-1"
|
||||||
|
>
|
||||||
|
15
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="0"
|
||||||
|
>
|
||||||
|
16 (default)
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="1"
|
||||||
|
>
|
||||||
|
17
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="2"
|
||||||
|
>
|
||||||
|
18
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="4"
|
||||||
|
>
|
||||||
|
20
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="6"
|
||||||
|
>
|
||||||
|
22
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="8"
|
||||||
|
>
|
||||||
|
24
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="10"
|
||||||
|
>
|
||||||
|
26
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="12"
|
||||||
|
>
|
||||||
|
28
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="14"
|
||||||
|
>
|
||||||
|
30
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="16"
|
||||||
|
>
|
||||||
|
32
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="18"
|
||||||
|
>
|
||||||
|
34
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="20"
|
||||||
|
>
|
||||||
|
36
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<label
|
||||||
|
for="mx_Field_1"
|
||||||
|
>
|
||||||
|
Font size
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_FontScalingPanel_preview mx_EventTilePreview_loader"
|
class="mx_FontScalingPanel_preview mx_EventTilePreview_loader"
|
||||||
>
|
>
|
||||||
|
@ -262,83 +373,6 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider_smallText"
|
|
||||||
>
|
|
||||||
Aa
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_Slider"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
aria-label="Font size"
|
|
||||||
autocomplete="off"
|
|
||||||
max="21"
|
|
||||||
min="11"
|
|
||||||
step="1"
|
|
||||||
type="range"
|
|
||||||
value="16"
|
|
||||||
/>
|
|
||||||
<output
|
|
||||||
class="mx_Slider_selection"
|
|
||||||
style="left: calc(2px + 50% + 1.2em - 1.2em);"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="mx_Slider_selection_label"
|
|
||||||
>
|
|
||||||
16
|
|
||||||
</span>
|
|
||||||
</output>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_FontScalingPanel_fontSlider_largeText"
|
|
||||||
>
|
|
||||||
Aa
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id="checkbox_QgU2PomxwK"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
for="checkbox_QgU2PomxwK"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Checkbox_background"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Checkbox_checkmark"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Use custom size
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="mx_Field mx_Field_input mx_AppearanceUserSettingsTab_checkboxControlledField"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
autocomplete="off"
|
|
||||||
disabled=""
|
|
||||||
id="font_size_field"
|
|
||||||
label="Font size"
|
|
||||||
placeholder="16"
|
|
||||||
type="number"
|
|
||||||
value="16"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
for="font_size_field"
|
|
||||||
>
|
|
||||||
Font size
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -710,8 +710,8 @@ describe("ElementCall", () => {
|
||||||
const originalGetValue = SettingsStore.getValue;
|
const originalGetValue = SettingsStore.getValue;
|
||||||
SettingsStore.getValue = <T>(name: string, roomId?: string, excludeDefault?: boolean) => {
|
SettingsStore.getValue = <T>(name: string, roomId?: string, excludeDefault?: boolean) => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "baseFontSizeV2":
|
case "fontSizeDelta":
|
||||||
return 12 as T;
|
return 4 as T;
|
||||||
case "useSystemFont":
|
case "useSystemFont":
|
||||||
return true as T;
|
return true as T;
|
||||||
case "systemFont":
|
case "systemFont":
|
||||||
|
@ -720,13 +720,14 @@ describe("ElementCall", () => {
|
||||||
return originalGetValue<T>(name, roomId, excludeDefault);
|
return originalGetValue<T>(name, roomId, excludeDefault);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
document.documentElement.style.fontSize = "12px";
|
||||||
|
|
||||||
await ElementCall.create(room);
|
await ElementCall.create(room);
|
||||||
const call = Call.get(room);
|
const call = Call.get(room);
|
||||||
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
|
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
|
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
|
||||||
expect(urlParams.get("fontScale")).toBe("0.75");
|
expect(urlParams.get("fontScale")).toBe("1.5");
|
||||||
expect(urlParams.getAll("font")).toEqual(["OpenDyslexic", "DejaVu Sans"]);
|
expect(urlParams.getAll("font")).toEqual(["OpenDyslexic", "DejaVu Sans"]);
|
||||||
|
|
||||||
SettingsStore.getValue = originalGetValue;
|
SettingsStore.getValue = originalGetValue;
|
||||||
|
|
|
@ -123,6 +123,7 @@ describe("FontWatcher", function () {
|
||||||
let watcher: FontWatcher | undefined;
|
let watcher: FontWatcher | undefined;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
document.documentElement.style.fontSize = "14px";
|
||||||
watcher = new FontWatcher();
|
watcher = new FontWatcher();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -132,13 +133,35 @@ describe("FontWatcher", function () {
|
||||||
|
|
||||||
it("should not run the migration", async () => {
|
it("should not run the migration", async () => {
|
||||||
await watcher!.start();
|
await watcher!.start();
|
||||||
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(16);
|
expect(SettingsStore.getValue("fontSizeDelta")).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should migrate to default font size", async () => {
|
it("should migrate from V1 font size to V3", async () => {
|
||||||
await SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, 13);
|
await SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, 13);
|
||||||
await watcher!.start();
|
await watcher!.start();
|
||||||
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(19);
|
// 13px (V1 font size) + 5px (V1 offset) + 1px (root font size increase) - 14px (default browser font size) = 5px
|
||||||
|
expect(SettingsStore.getValue("fontSizeDelta")).toBe(5);
|
||||||
|
// baseFontSize should be cleared
|
||||||
|
expect(SettingsStore.getValue("baseFontSize")).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should migrate from V2 font size to V3 using browser font size", async () => {
|
||||||
|
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, 18);
|
||||||
|
await watcher!.start();
|
||||||
|
// 18px - 14px (default browser font size) = 2px
|
||||||
|
expect(SettingsStore.getValue("fontSizeDelta")).toBe(4);
|
||||||
|
// baseFontSize should be cleared
|
||||||
|
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should migrate from V2 font size to V3 using fallback font size", async () => {
|
||||||
|
document.documentElement.style.fontSize = "";
|
||||||
|
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, 18);
|
||||||
|
await watcher!.start();
|
||||||
|
// 18px - 16px (fallback) = 2px
|
||||||
|
expect(SettingsStore.getValue("fontSizeDelta")).toBe(2);
|
||||||
|
// baseFontSize should be cleared
|
||||||
|
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue