From c3924d56c20cc2769ac353ead4e0b3b9e488059e Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Tue, 9 Aug 2022 17:32:37 +0100 Subject: [PATCH 1/2] Update outdated screenshot in `Renaming a repository` (#29755) * update screenshot * Optimize images Co-authored-by: github-actions --- .../repository/repository-name-change.png | Bin 14983 -> 11550 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/images/help/repository/repository-name-change.png b/assets/images/help/repository/repository-name-change.png index 8deb3709927aa52e8677198dfeef9a5a2026f150..f828f77b279497d22c32f97b5620e6d694356b18 100644 GIT binary patch literal 11550 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 Yi@|m>ElxX=4|^^vsU%S#ZuIg00h7TVQ2+n{ literal 14983 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 V$V#WQ3V@X$6$K6XO4-)|{}&_sdiekV From 04f38e4779effde49eac793f98ff692cf9a971a5 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Tue, 9 Aug 2022 09:48:18 -0700 Subject: [PATCH 2/2] Parse color_mode cookie in browser (#29738) * Parse color_mode cookie in browser * Update useTheme.ts * Update use-theme.js * Update use-theme.js * Add support for dark_high_contrast Per https://primer.style/css/support/theming I also checked all the color mode options, this is the only additional working with Primer 20 so far * Remove gray bg * Remove type * Use defaults instead of types for fn args * Thicker types --- components/hooks/useSession.ts | 11 ---- components/hooks/useTheme.ts | 114 +++++++++++++++++++++++++++++++++ lib/get-theme.js | 66 ------------------- middleware/api/session.js | 3 - pages/_app.tsx | 22 +++---- stylesheets/index.scss | 6 -- tests/unit/get-theme.js | 80 ----------------------- tests/unit/use-theme.js | 39 +++++++++++ 8 files changed, 163 insertions(+), 178 deletions(-) create mode 100644 components/hooks/useTheme.ts delete mode 100644 lib/get-theme.js delete mode 100644 tests/unit/get-theme.js create mode 100644 tests/unit/use-theme.js diff --git a/components/hooks/useSession.ts b/components/hooks/useSession.ts index f9119798a1..7bbbfaaca9 100644 --- a/components/hooks/useSession.ts +++ b/components/hooks/useSession.ts @@ -1,4 +1,3 @@ -import type { ThemeProviderProps } from '@primer/react' import { useEffect } from 'react' import useSWR from 'swr' @@ -14,16 +13,6 @@ export type Session = { isSignedIn: boolean csrfToken?: string userLanguage: string // en, es, ja, cn - theme: { - colorMode: Pick - nightTheme: string - dayTheme: string - } - themeCss: { - colorMode: Pick - nightTheme: string - dayTheme: string - } } // React hook version diff --git a/components/hooks/useTheme.ts b/components/hooks/useTheme.ts new file mode 100644 index 0000000000..b8134a0e57 --- /dev/null +++ b/components/hooks/useTheme.ts @@ -0,0 +1,114 @@ +import { useState, useEffect } from 'react' +import Cookies from 'js-cookie' + +enum CssColorMode { + auto = 'auto', + light = 'light', + dark = 'dark', +} + +enum ComponentColorMode { + auto = 'auto', + day = 'day', + night = 'night', +} + +enum SupportedTheme { + light = 'light', + dark = 'dark', + dark_dimmed = 'dark_dimmed', + dark_high_contrast = 'dark_high_contrast', +} + +type CssColorTheme = { + colorMode: CssColorMode + lightTheme: SupportedTheme + darkTheme: SupportedTheme +} + +type ComponentColorTheme = { + colorMode: ComponentColorMode + dayScheme: SupportedTheme + nightScheme: SupportedTheme +} + +type ColorModeThemes = { + css: CssColorTheme + component: ComponentColorTheme +} + +export const defaultCSSTheme: CssColorTheme = { + colorMode: CssColorMode.auto, + lightTheme: SupportedTheme.light, + darkTheme: SupportedTheme.dark, +} + +export const defaultComponentTheme: ComponentColorTheme = { + colorMode: ComponentColorMode.auto, + dayScheme: SupportedTheme.light, + nightScheme: SupportedTheme.dark, +} + +const cssColorModeToComponentColorMode: Record = { + [CssColorMode.auto]: ComponentColorMode.auto, + [CssColorMode.light]: ComponentColorMode.day, + [CssColorMode.dark]: ComponentColorMode.night, +} + +function filterMode(mode = ''): CssColorMode | undefined { + if (Object.values(CssColorMode).includes(mode)) { + return mode as CssColorMode + } +} + +function filterTheme({ name = '', color_mode = '' } = {}): SupportedTheme | undefined { + if (Object.values(SupportedTheme).includes(name)) { + return name as SupportedTheme + } + if (Object.values(SupportedTheme).includes(color_mode)) { + return color_mode as SupportedTheme + } +} + +export function getCssTheme(cookieValue = ''): CssColorTheme { + if (!cookieValue) return defaultCSSTheme + try { + const parsed = JSON.parse(cookieValue) + const { color_mode, light_theme, dark_theme } = parsed + return { + colorMode: filterMode(color_mode) || defaultCSSTheme.colorMode, + lightTheme: filterTheme(light_theme) || defaultCSSTheme.lightTheme, + darkTheme: filterTheme(dark_theme) || defaultCSSTheme.darkTheme, + } + } catch (err) { + console.warn("Unable to parse 'color_mode' cookie", err) + return defaultCSSTheme + } +} + +export function getComponentTheme(cookieValue = ''): ComponentColorTheme { + const { colorMode, lightTheme, darkTheme } = getCssTheme(cookieValue) + return { + // The cookie value is a primer/css color_mode. + // We need to convert that to a primer/react compatible version. + colorMode: cssColorModeToComponentColorMode[colorMode], + dayScheme: lightTheme, + nightScheme: darkTheme, + } +} + +export function useTheme() { + const [theme, setTheme] = useState({ + css: defaultCSSTheme, + component: defaultComponentTheme, + }) + + useEffect(() => { + const cookieValue = Cookies.get('color_mode') + const css = getCssTheme(cookieValue) + const component = getComponentTheme(cookieValue) + setTheme({ css, component }) + }, []) + + return { theme } +} diff --git a/lib/get-theme.js b/lib/get-theme.js deleted file mode 100644 index a4b9b964dc..0000000000 --- a/lib/get-theme.js +++ /dev/null @@ -1,66 +0,0 @@ -export const defaultCSSTheme = { - colorMode: 'auto', // light, dark, auto - nightTheme: 'dark', - dayTheme: 'light', -} - -export const defaultComponentTheme = { - colorMode: 'auto', // day, night, auto - nightTheme: 'dark', - dayTheme: 'light', -} - -const cssColorModeToComponentColorMode = { - auto: 'auto', - light: 'day', - dark: 'night', -} - -const supportedThemes = ['light', 'dark', 'dark_dimmed'] - -/* - * Our version of primer/css is out of date, so we can only support known themes. - * For the least jarring experience possible, we fallback to the color_mode (light / dark) if provided by the theme, otherwise our own defaults - */ -function getSupportedTheme(theme, fallbackTheme) { - if (!theme) { - return fallbackTheme - } - - return supportedThemes.includes(theme.name) ? theme.name : theme.color_mode -} - -/* - * Returns theme for consumption by either primer/css or primer/components - * based on the cookie and/or fallback values - */ -export function getTheme(req, cssMode = false) { - const cookieValue = {} - - const defaultTheme = cssMode ? defaultCSSTheme : defaultComponentTheme - - if (req.cookies?.color_mode) { - try { - const parsed = JSON.parse(decodeURIComponent(req.cookies.color_mode)) - cookieValue.color_mode = parsed.color_mode - cookieValue.dark_theme = parsed.dark_theme - cookieValue.light_theme = parsed.light_theme - } catch (err) { - if (process.env.NODE_ENV !== 'test') { - console.warn("Unable to parse 'color_mode' cookie", err) - } - } - } - - // The cookie value is a primer/css color_mode. sometimes we need to convert that to a primer/components compatible version - const colorMode = - (cssMode - ? cookieValue.color_mode - : cssColorModeToComponentColorMode[cookieValue.color_mode || '']) || defaultTheme.colorMode - - return { - colorMode, - nightTheme: getSupportedTheme(cookieValue.dark_theme, defaultTheme.nightTheme), - dayTheme: getSupportedTheme(cookieValue.light_theme, defaultTheme.dayTheme), - } -} diff --git a/middleware/api/session.js b/middleware/api/session.js index c6d5e65ea8..4fe5d86f5c 100644 --- a/middleware/api/session.js +++ b/middleware/api/session.js @@ -1,5 +1,4 @@ import express from 'express' -import { getTheme } from '../../lib/get-theme.js' import { cacheControlFactory } from '../cache-control.js' const router = express.Router() @@ -11,8 +10,6 @@ router.get('/', (req, res) => { isSignedIn: Boolean(req.cookies?.dotcom_user), csrfToken: req.csrfToken?.() || '', userLanguage: req.userLanguage, - theme: getTheme(req), - themeCss: getTheme(req, true), }) }) diff --git a/pages/_app.tsx b/pages/_app.tsx index 24d691f143..bb9a4597b7 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import App from 'next/app' import type { AppProps, AppContext } from 'next/app' import Head from 'next/head' -import { ThemeProvider, SSRProvider, ThemeProviderProps } from '@primer/react' +import { ThemeProvider, SSRProvider } from '@primer/react' import '../stylesheets/index.scss' @@ -10,16 +10,17 @@ import { initializeEvents } from 'components/lib/events' import experiment from 'components/lib/experiment' import { LanguagesContext, LanguagesContextT } from 'components/context/LanguagesContext' import { useSession } from 'components/hooks/useSession' +import { useTheme } from 'components/hooks/useTheme' type MyAppProps = AppProps & { isDotComAuthenticated: boolean languagesContext: LanguagesContextT } -type colorModeAuto = Pick - const MyApp = ({ Component, pageProps, languagesContext }: MyAppProps) => { const { session } = useSession() + const { theme } = useTheme() + useEffect(() => { if (session?.csrfToken) { initializeEvents(session.csrfToken) @@ -54,18 +55,15 @@ const MyApp = ({ Component, pageProps, languagesContext }: MyAppProps) => { {/* Appears Next.js can't modify after server rendering: https://stackoverflow.com/a/54774431 */}
diff --git a/stylesheets/index.scss b/stylesheets/index.scss index a4f3e1b7b7..d4f295dc41 100644 --- a/stylesheets/index.scss +++ b/stylesheets/index.scss @@ -13,9 +13,3 @@ @import "shadows.scss"; @import "syntax-highlighting.scss"; @import "utilities.scss"; - -// render a mostly gray background until we know the color mode via XHR -html, -body { - background: #6e7781; -} diff --git a/tests/unit/get-theme.js b/tests/unit/get-theme.js deleted file mode 100644 index aba0a20bcb..0000000000 --- a/tests/unit/get-theme.js +++ /dev/null @@ -1,80 +0,0 @@ -import { describe, expect, test } from '@jest/globals' - -import { getTheme, defaultCSSTheme, defaultComponentTheme } from '../../lib/get-theme.js' - -function serializeCookieValue(obj) { - return encodeURIComponent(JSON.stringify(obj)) -} - -describe('getTheme basics', () => { - test('always return an object with certain keys', () => { - const req = {} // doesn't even have a `.cookies`. - const theme = getTheme(req) - expect(theme.colorMode).toBe(defaultComponentTheme.colorMode) - expect(theme.nightTheme).toBe(defaultComponentTheme.nightTheme) - expect(theme.dayTheme).toBe(defaultComponentTheme.dayTheme) - const cssTheme = getTheme(req, true) - expect(cssTheme.colorMode).toBe(defaultCSSTheme.colorMode) - expect(cssTheme.nightTheme).toBe(defaultCSSTheme.nightTheme) - expect(cssTheme.dayTheme).toBe(defaultCSSTheme.dayTheme) - }) - - test('respect the color_mode cookie value', () => { - const req = { - cookies: { - color_mode: serializeCookieValue({ - color_mode: 'dark', - light_theme: { name: 'light_colorblind', color_mode: 'light' }, - dark_theme: { name: 'dark_tritanopia', color_mode: 'dark' }, - }), - }, - } - const theme = getTheme(req) - expect(theme.colorMode).toBe('night') - expect(theme.nightTheme).toBe(defaultComponentTheme.nightTheme) - expect(theme.dayTheme).toBe(defaultComponentTheme.dayTheme) - - const cssTheme = getTheme(req, true) - expect(cssTheme.colorMode).toBe('dark') - expect(cssTheme.nightTheme).toBe(defaultCSSTheme.nightTheme) - expect(cssTheme.dayTheme).toBe(defaultCSSTheme.dayTheme) - }) - - test('respect the color_mode cookie value', () => { - const req = { - cookies: { - color_mode: serializeCookieValue({ - color_mode: 'dark', - light_theme: { name: 'light_colorblind', color_mode: 'light' }, - dark_theme: { name: 'dark_tritanopia', color_mode: 'dark' }, - }), - }, - } - const theme = getTheme(req) - expect(theme.colorMode).toBe('night') - expect(theme.nightTheme).toBe(defaultComponentTheme.nightTheme) - expect(theme.dayTheme).toBe(defaultComponentTheme.dayTheme) - - const cssTheme = getTheme(req, true) - expect(cssTheme.colorMode).toBe('dark') - expect(cssTheme.nightTheme).toBe(defaultCSSTheme.nightTheme) - expect(cssTheme.dayTheme).toBe(defaultCSSTheme.dayTheme) - }) - - test('ignore "junk" cookie values', () => { - const req = { - cookies: { - color_mode: '[This is not valid JSON}', - }, - } - const theme = getTheme(req) - expect(theme.colorMode).toBe('auto') - expect(theme.nightTheme).toBe(defaultComponentTheme.nightTheme) - expect(theme.dayTheme).toBe(defaultComponentTheme.dayTheme) - - const cssTheme = getTheme(req, true) - expect(cssTheme.colorMode).toBe('auto') - expect(cssTheme.nightTheme).toBe(defaultCSSTheme.nightTheme) - expect(cssTheme.dayTheme).toBe(defaultCSSTheme.dayTheme) - }) -}) diff --git a/tests/unit/use-theme.js b/tests/unit/use-theme.js new file mode 100644 index 0000000000..76b050c36d --- /dev/null +++ b/tests/unit/use-theme.js @@ -0,0 +1,39 @@ +import { describe, expect, test } from '@jest/globals' +import { + getComponentTheme, + getCssTheme, + defaultCSSTheme, + defaultComponentTheme, +} from '../../components/hooks/useTheme.ts' + +describe('getTheme basics', () => { + test('always return an object with certain keys', () => { + const cookieValue = JSON.stringify({}) + expect(getCssTheme(cookieValue)).toEqual(defaultCSSTheme) + expect(getComponentTheme(cookieValue)).toEqual(defaultComponentTheme) + }) + + test('ignore "junk" cookie values', () => { + const cookieValue = '[This is not valid JSON}' + expect(getCssTheme(cookieValue)).toEqual(defaultCSSTheme) + expect(getComponentTheme(cookieValue)).toEqual(defaultComponentTheme) + }) + + test('respect the color_mode cookie value', () => { + const cookieValue = JSON.stringify({ + color_mode: 'dark', + light_theme: { name: 'light_colorblind', color_mode: 'light' }, + dark_theme: { name: 'dark_tritanopia', color_mode: 'dark' }, + }) + + const cssTheme = getCssTheme(cookieValue) + expect(cssTheme.colorMode).toBe('dark') + expect(cssTheme.darkTheme).toBe(defaultCSSTheme.darkTheme) + expect(cssTheme.lightTheme).toBe(defaultCSSTheme.lightTheme) + + const componentTheme = getComponentTheme(cookieValue) + expect(componentTheme.colorMode).toBe('night') + expect(componentTheme.nightScheme).toBe(defaultComponentTheme.nightScheme) + expect(componentTheme.dayScheme).toBe(defaultComponentTheme.dayScheme) + }) +})