Skip to content

Commit

Permalink
Init dark mode with feature flag and body class (#4687)
Browse files Browse the repository at this point in the history
  • Loading branch information
zackkrida authored Aug 2, 2024
1 parent 88ea037 commit 43aac27
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
9 changes: 9 additions & 0 deletions frontend/feat/feature-flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
"description": "Show results marked as sensitive in the results area.",
"supportsQuery": false,
"storage": "session"
},
"force_dark_mode": {
"status": {
"staging": "switchable",
"production": "disabled"
},
"defaultState": "off",
"description": "Force the site to render in dark mode.",
"storage": "session"
}
},
"groups": [
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/app.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { computed, onMounted, useLocaleHead } from "#imports"
import { computed, onMounted, useLocaleHead, useHead } from "#imports"
import { useUiStore } from "~/stores/ui"
import { useFeatureFlagStore } from "~/stores/feature-flag"
import { useLayout } from "~/composables/use-layout"
import { useDarkMode } from "~/composables/use-dark-mode"
import VSkipToContentButton from "~/components/VSkipToContentButton.vue"
Expand All @@ -13,6 +14,8 @@ const { updateBreakpoint } = useLayout()
const featureFlagStore = useFeatureFlagStore()
const uiStore = useUiStore()
const darkMode = useDarkMode()
/* UI store */
const isDesktopLayout = computed(() => uiStore.isDesktopLayout)
const breakpoint = computed(() => uiStore.breakpoint)
Expand All @@ -23,6 +26,10 @@ const head = useLocaleHead({
addSeoAttributes: true,
})
useHead({
bodyAttrs: { class: darkMode.cssClass },
})
/**
* Update the breakpoint value in the cookie on mounted.
* The Pinia state might become different from the cookie state if, for example, the cookies were saved when the screen was `sm`,
Expand Down
26 changes: 26 additions & 0 deletions frontend/src/composables/use-dark-mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { computed } from "#imports"

import { useFeatureFlagStore } from "~/stores/feature-flag"

export const DARK_MODE_CLASS = "dark-mode"
export const LIGHT_MODE_CLASS = "light-mode"

/**
* TODO: Replace with the user's actual dark mode preference.
* Dark mode detection will be based on user preference,
* overwritten by the "force_dark_mode" feature flag.
*/
export function useDarkMode() {
const featureFlagStore = useFeatureFlagStore()

const isDarkMode = computed(() => featureFlagStore.isOn("force_dark_mode"))
const cssClass = computed(() =>
isDarkMode.value ? DARK_MODE_CLASS : LIGHT_MODE_CLASS
)

return {
isDarkMode,
/** The CSS class representing the current color mode. */
cssClass,
}
}
35 changes: 35 additions & 0 deletions frontend/test/unit/specs/composables/use-dark-mode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, it, expect } from "vitest"

import {
DARK_MODE_CLASS,
LIGHT_MODE_CLASS,
useDarkMode,
} from "~/composables/use-dark-mode"
import { OFF, ON } from "~/constants/feature-flag"
import { useFeatureFlagStore } from "~/stores/feature-flag"

describe("useDarkMode", () => {
it(`should report isDarkMode as true and cssClass as ${DARK_MODE_CLASS} when the feature flag is enabled`, () => {
const featureFlagStore = useFeatureFlagStore()
featureFlagStore.toggleFeature("force_dark_mode", ON)

// Call the composable
const { isDarkMode, cssClass } = useDarkMode()

// Assert the computed properties
expect(isDarkMode.value).toBe(true)
expect(cssClass.value).toBe(DARK_MODE_CLASS)
})

it(`should report isDarkMode as false and cssClass as ${LIGHT_MODE_CLASS} when the feature flag is disabled`, () => {
const featureFlagStore = useFeatureFlagStore()
featureFlagStore.toggleFeature("force_dark_mode", OFF)

// Call the composable
const { isDarkMode, cssClass } = useDarkMode()

// Assert the computed properties
expect(isDarkMode.value).toBe(false)
expect(cssClass.value).toBe(LIGHT_MODE_CLASS)
})
})

0 comments on commit 43aac27

Please sign in to comment.