Fix bundled font or custom font not applied after theme switch (#31591)

* refactor: transform `FontWater.onAction` to switch

* fix: reload font after switching theme

Fix #26248 #31588

When a theme is swiched, `clearCustomTheme` remove all css variables.
After the styles are re-applied but the custom fonts or emoji are not
re-applied.

* test: add test for `Action.ReloadFont`

* test: add missing tests for existing actions

* test(e2e): add tests to ensure that font and emoji stay unchanged

* Revert "fix: reload font after switching theme"

This reverts commit 2b0071af21c38bf2c86780356aa39d290e9d9148.

* Revert "refactor: transform `FontWater.onAction` to switch"

This reverts commit 411915860923230cabce3ad5498fb46696a9a65e.

* Revert "test: add test for `Action.ReloadFont`"

This reverts commit 31b3b224cd2c443663a2b9bba312a4140907a8ed.

* fix: don't remove custom emoji and cpd font when clearing custom theme

Fix #26248 #31588

When a theme is swiched, `clearCustomTheme` remove all css variables.
After the styles are re-applied but the custom fonts or emoji are not
re-applied.
This fix avoid the custom font and emoji to be removed.

* test: add tests
This commit is contained in:
Florian Duros
2026-01-05 17:14:08 +01:00
committed by GitHub
parent 4bd1d4144f
commit be7be39d0f
7 changed files with 113 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { sleep } from "matrix-js-sdk/src/utils";
import { waitFor } from "jest-matrix-react";
import SettingsStore from "../../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../../src/settings/SettingLevel";
@@ -155,5 +156,33 @@ describe("FontWatcher", function () {
// baseFontSize should be cleared
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(0);
});
it("should trigger migration when dispatched", async () => {
await watcher!.start();
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, 18);
defaultDispatcher.fire(Action.MigrateBaseFontSize);
await waitFor(() => {
// 18px - 16px (default browser font size) = 2px
expect(SettingsStore.getValue("fontSizeDelta")).toBe(2);
// baseFontSizeV2 should be cleared
expect(SettingsStore.getValue("baseFontSizeV2")).toBe(0);
});
});
});
it("should update root font size with positive delta", async () => {
await new FontWatcher().start();
defaultDispatcher.dispatch({
action: Action.UpdateFontSizeDelta,
delta: 2,
});
await waitFor(() => {
const rootFontSize = document.querySelector<HTMLElement>(":root")!.style.fontSize;
expect(rootFontSize).toContain("2px");
});
});
});

View File

@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import SettingsStore from "../../src/settings/SettingsStore";
import { FontWatcher } from "../../src/settings/watchers/FontWatcher";
import { enumerateThemes, getOrderedThemes, setTheme } from "../../src/theme";
describe("theme", () => {
@@ -223,4 +224,45 @@ describe("theme", () => {
]);
});
});
describe("clearCustomTheme", () => {
beforeEach(() => {
// Reset document state
document.body.style.cssText = "";
document.head.querySelectorAll("style[title^='custom-theme-']").forEach((el) => el.remove());
});
it("should not remove font family custom properties", async () => {
// Mock theme elements
const lightTheme = {
dataset: { mxTheme: "light" },
disabled: true,
href: "fake URL",
onload: (): void => void 0,
} as unknown as HTMLStyleElement;
const removePropertySpy = jest.fn();
const styleObject = {
0: FontWatcher.FONT_FAMILY_CUSTOM_PROPERTY,
1: FontWatcher.EMOJI_FONT_FAMILY_CUSTOM_PROPERTY,
2: "--custom-color",
length: 3,
removeProperty: removePropertySpy,
};
jest.spyOn(document.body, "style", "get").mockReturnValue(styleObject as any);
jest.spyOn(document, "querySelectorAll").mockReturnValue([lightTheme] as any);
// Trigger clearCustomTheme via setTheme
await new Promise((resolve) => {
setTheme("light").then(resolve);
lightTheme.onload!({} as Event);
});
// Check that font properties were NOT removed
expect(removePropertySpy).not.toHaveBeenCalledWith(FontWatcher.FONT_FAMILY_CUSTOM_PROPERTY);
expect(removePropertySpy).not.toHaveBeenCalledWith(FontWatcher.EMOJI_FONT_FAMILY_CUSTOM_PROPERTY);
// But custom color should be removed
expect(removePropertySpy).toHaveBeenCalledWith("--custom-color");
});
});
});