From f4bd777166e2ade4e3850dbbc955e04b27486f51 Mon Sep 17 00:00:00 2001 From: Dhruv Bhanushali Date: Tue, 3 Sep 2024 22:20:16 +0530 Subject: [PATCH] Add handling for OS color mode to the composable --- frontend/src/composables/use-dark-mode.ts | 38 +++++++++++++++++++ .../specs/composables/use-dark-mode.spec.ts | 36 +++++++++++++----- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/frontend/src/composables/use-dark-mode.ts b/frontend/src/composables/use-dark-mode.ts index d47ceb71366..598da0ed0b0 100644 --- a/frontend/src/composables/use-dark-mode.ts +++ b/frontend/src/composables/use-dark-mode.ts @@ -1,5 +1,7 @@ import { computed, useUiStore } from "#imports" +import { usePreferredColorScheme } from "@vueuse/core" + import { useFeatureFlagStore } from "~/stores/feature-flag" export const DARK_MODE_CLASS = "dark-mode" @@ -22,6 +24,12 @@ export function useDarkMode() { featureFlagStore.isOn("dark_mode_ui_toggle") ) + /** + * the color mode setting for the app; + * + * This can be one of "dark", "light" or "system". If the toggle + * feature is disabled, we default to "light". + */ const colorMode = computed(() => { if (darkModeToggleable.value) { return uiStore.colorMode @@ -29,6 +37,34 @@ export function useDarkMode() { return "light" }) + /** + * the color mode setting for the OS; + * + * This can be one of "dark" or "light". If the OS does not specify + * a preference, we default to "light". + */ + const osColorMode = computed(() => { + const pref = usePreferredColorScheme() + return pref.value === "no-preference" ? "light" : pref.value + }) + + /** + * the effective color mode of the app; + * + * This can be one of "dark" or "light". This is a combination of the + * toggle feature flag, the user's preference at the app and OS levels + * and the default value of "light". + */ + const effectiveColorMode = computed(() => { + if (!darkModeToggleable.value) { + return "light" + } + if (colorMode.value === "system") { + return osColorMode.value + } + return colorMode.value + }) + const cssClass = computed(() => { return { light: LIGHT_MODE_CLASS, @@ -39,6 +75,8 @@ export function useDarkMode() { return { colorMode, + osColorMode, + effectiveColorMode, cssClass, } } diff --git a/frontend/test/unit/specs/composables/use-dark-mode.spec.ts b/frontend/test/unit/specs/composables/use-dark-mode.spec.ts index fcea64a7a05..6d4d50386dc 100644 --- a/frontend/test/unit/specs/composables/use-dark-mode.spec.ts +++ b/frontend/test/unit/specs/composables/use-dark-mode.spec.ts @@ -1,4 +1,6 @@ -import { describe, expect, test } from "vitest" +import { describe, expect, test, vi } from "vitest" + +import { usePreferredColorScheme } from "@vueuse/core" import { DARK_MODE_CLASS, @@ -9,16 +11,31 @@ import { OFF, ON } from "~/constants/feature-flag" import { useFeatureFlagStore } from "~/stores/feature-flag" import { useUiStore } from "~/stores/ui" +vi.mock("@vueuse/core", () => ({ + usePreferredColorScheme: vi.fn(), +})) + describe("useDarkMode", () => { test.each` - description | featureFlags | uiColorMode | expectedColorMode | expectedCssClass - ${"Disable toggling"} | ${{ dark_mode_ui_toggle: OFF }} | ${"dark"} | ${"light"} | ${LIGHT_MODE_CLASS} - ${"Enable toggling, User preference: light"} | ${{ dark_mode_ui_toggle: ON }} | ${"light"} | ${"light"} | ${LIGHT_MODE_CLASS} - ${"Enable toggling, User preference: dark"} | ${{ dark_mode_ui_toggle: ON }} | ${"dark"} | ${"dark"} | ${DARK_MODE_CLASS} - ${"Enable toggling, User preference: system"} | ${{ dark_mode_ui_toggle: ON }} | ${"system"} | ${"system"} | ${""} + description | featureFlags | uiColorMode | osColorMode | expectedColorMode | expectedEffectiveColorMode | expectedCssClass + ${"Toggle: off"} | ${{ dark_mode_ui_toggle: OFF }} | ${"dark"} | ${"dark"} | ${"light"} | ${"light"} | ${LIGHT_MODE_CLASS} + ${"Toggle: on, Preference: light"} | ${{ dark_mode_ui_toggle: ON }} | ${"light"} | ${"dark"} | ${"light"} | ${"light"} | ${LIGHT_MODE_CLASS} + ${"Toggle: on, Preference: dark"} | ${{ dark_mode_ui_toggle: ON }} | ${"dark"} | ${"light"} | ${"dark"} | ${"dark"} | ${DARK_MODE_CLASS} + ${"Toggle: on, Preference: system, System: light"} | ${{ dark_mode_ui_toggle: ON }} | ${"system"} | ${"light"} | ${"system"} | ${"light"} | ${""} + ${"Toggle: on, Preference: system, System: dark"} | ${{ dark_mode_ui_toggle: ON }} | ${"system"} | ${"dark"} | ${"system"} | ${"dark"} | ${""} + ${"Toggle: on, Preference: system, System: no-preference"} | ${{ dark_mode_ui_toggle: ON }} | ${"system"} | ${"no-preference"} | ${"system"} | ${"light"} | ${""} `( - "$description: should report colorMode as $expectedColorMode and cssClass as $expectedCssClass", - ({ featureFlags, uiColorMode, expectedColorMode, expectedCssClass }) => { + "$description: should report colorMode as $expectedColorMode, effectiveColorMode as $expectedEffectiveColorMode and cssClass as $expectedCssClass", + ({ + featureFlags, + uiColorMode, + osColorMode, + expectedColorMode, + expectedEffectiveColorMode, + expectedCssClass, + }) => { + usePreferredColorScheme.mockImplementation(() => ({ value: osColorMode })) + const featureFlagStore = useFeatureFlagStore() featureFlagStore.toggleFeature( @@ -31,10 +48,11 @@ describe("useDarkMode", () => { uiStore.colorMode = uiColorMode // Call the composable - const { colorMode, cssClass } = useDarkMode() + const { colorMode, effectiveColorMode, cssClass } = useDarkMode() // Assert the computed properties expect(colorMode.value).toBe(expectedColorMode) + expect(effectiveColorMode.value).toBe(expectedEffectiveColorMode) expect(cssClass.value).toBe(expectedCssClass) } )