From a68a41cded98065005d825f4e26e388905b0c4cf Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Thu, 21 Mar 2024 16:23:58 +0100 Subject: [PATCH 1/2] fix: theme loading when no matching theme found Fixes theme loading when no matching theme for the current OS settings is found. --- .../unreleased/bugfix-theme-loading-without-matching-theme | 6 ++++++ packages/web-pkg/src/composables/piniaStores/theme.ts | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/bugfix-theme-loading-without-matching-theme diff --git a/changelog/unreleased/bugfix-theme-loading-without-matching-theme b/changelog/unreleased/bugfix-theme-loading-without-matching-theme new file mode 100644 index 00000000000..e7122353fde --- /dev/null +++ b/changelog/unreleased/bugfix-theme-loading-without-matching-theme @@ -0,0 +1,6 @@ +Bugfix: Theme loading without matching theme + +We've fixed an issue where theme loading would break when there was no matching oC theme found for the user's OS setting. For example, this occurred when a user's OS setting was configured to "dark," but the instance of oC did not offer a dark theme. + +https://github.com/owncloud/web/issues/10657 +https://github.com/owncloud/web/pull/10659 diff --git a/packages/web-pkg/src/composables/piniaStores/theme.ts b/packages/web-pkg/src/composables/piniaStores/theme.ts index 335ed272f10..83b922dcace 100644 --- a/packages/web-pkg/src/composables/piniaStores/theme.ts +++ b/packages/web-pkg/src/composables/piniaStores/theme.ts @@ -104,7 +104,9 @@ export const useThemeStore = defineStore('theme', () => { availableThemes.value = themeConfig.themes.map((theme) => merge(themeConfig.defaults, theme)) if (unref(currentThemeName) === null) { - currentThemeName.value = unref(availableThemes).find((t) => t.isDark === unref(isDark)).name + const theme = + unref(availableThemes).find((t) => t.isDark === unref(isDark)) || unref(availableThemes)[0] + currentThemeName.value = theme.name } setAndApplyTheme( From 488d91a5d1fc3ac7179239753bca32e4d9346260 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Fri, 22 Mar 2024 08:52:08 +0100 Subject: [PATCH 2/2] test: add unit tests for theme store --- .../src/composables/piniaStores/theme.ts | 2 +- .../composables/piniaStores/theme.spec.ts | 82 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 packages/web-pkg/tests/unit/composables/piniaStores/theme.spec.ts diff --git a/packages/web-pkg/src/composables/piniaStores/theme.ts b/packages/web-pkg/src/composables/piniaStores/theme.ts index 83b922dcace..f83f8c5536e 100644 --- a/packages/web-pkg/src/composables/piniaStores/theme.ts +++ b/packages/web-pkg/src/composables/piniaStores/theme.ts @@ -77,7 +77,7 @@ export const ThemingConfig = z.object({ }) type WebThemeType = z.infer -type WebThemeConfigType = z.infer +export type WebThemeConfigType = z.infer const themeStorageKey = 'oc_currentThemeName' diff --git a/packages/web-pkg/tests/unit/composables/piniaStores/theme.spec.ts b/packages/web-pkg/tests/unit/composables/piniaStores/theme.spec.ts new file mode 100644 index 00000000000..5cb9a9547a7 --- /dev/null +++ b/packages/web-pkg/tests/unit/composables/piniaStores/theme.spec.ts @@ -0,0 +1,82 @@ +import { useLocalStorage, usePreferredDark } from '@vueuse/core' +import { useThemeStore, WebThemeConfigType } from '../../../../src/composables/piniaStores' +import { mockDeep } from 'jest-mock-extended' +import { createPinia, setActivePinia } from 'pinia' +import { ref } from 'vue' + +jest.mock('@vueuse/core', () => { + return { useLocalStorage: jest.fn(() => ref('')), usePreferredDark: jest.fn(() => ref(false)) } +}) + +describe('useThemeStore', () => { + beforeEach(() => { + setActivePinia(createPinia()) + }) + + describe('initializeThemes', () => { + it('sets availableThemes', () => { + const themeConfig = mockDeep() + themeConfig.themes = [ + { name: 'light', designTokens: {}, isDark: false }, + { name: 'dark', designTokens: {}, isDark: true } + ] + + const store = useThemeStore() + store.initializeThemes(themeConfig) + + expect(store.availableThemes.length).toBe(themeConfig.themes.length) + }) + describe('currentTheme', () => { + it.each([true, false])('gets set based on the OS setting', (isDark) => { + jest.mocked(usePreferredDark).mockReturnValue(ref(isDark)) + jest.mocked(useLocalStorage).mockReturnValue(ref(null)) + + const themeConfig = mockDeep() + themeConfig.themes = [ + { name: 'light', designTokens: {}, isDark: false }, + { name: 'dark', designTokens: {}, isDark: true } + ] + themeConfig.defaults = {} + + const store = useThemeStore() + store.initializeThemes(themeConfig) + + expect(store.currentTheme.name).toEqual( + themeConfig.themes.find((t) => t.isDark === isDark).name + ) + }) + it('falls back to the first theme if no match for the OS setting is found', () => { + jest.mocked(usePreferredDark).mockReturnValue(ref(true)) + jest.mocked(useLocalStorage).mockReturnValue(ref(null)) + + const themeConfig = mockDeep() + themeConfig.themes = [{ name: 'light', designTokens: {}, isDark: false }] + themeConfig.defaults = {} + + const store = useThemeStore() + store.initializeThemes(themeConfig) + + expect(store.currentTheme.name).toEqual('light') + }) + }) + describe('toggleTheme', () => { + it('toggles the themes between light and dark mode', () => { + const themeConfig = mockDeep() + themeConfig.defaults = {} + themeConfig.themes = [ + { name: 'light', designTokens: {}, isDark: false }, + { name: 'dark', designTokens: {}, isDark: true } + ] + + const store = useThemeStore() + store.initializeThemes(themeConfig) + + expect(store.currentTheme.name).toEqual('light') + + store.toggleTheme() + + expect(store.currentTheme.name).toEqual('dark') + }) + }) + }) +})