Skip to content

Commit

Permalink
adds resolved color scheme to theme context (#1679)
Browse files Browse the repository at this point in the history
* Add `resolvedColorScheme` property to the object returned by `useTheme()

* Add `resolvedColorScheme` property to the object returned by `useTheme()

* Update .changeset/red-bottles-prove.md

Co-authored-by: Cole Bemis <colebemis@github.com>
  • Loading branch information
2 people authored and pksjce committed Dec 19, 2021
1 parent e476adf commit e292877
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-bottles-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/components': minor
---

Add `resolvedColorScheme` property to the object returned by `useTheme()`
27 changes: 22 additions & 5 deletions src/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ThemeContext = React.createContext<{
colorScheme?: string
colorMode?: ColorModeWithAuto
resolvedColorMode?: ColorMode
resolvedColorScheme?: string
dayScheme?: string
nightScheme?: string
setColorMode: React.Dispatch<React.SetStateAction<ColorModeWithAuto>>
Expand Down Expand Up @@ -52,7 +53,10 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({children, ...props}
const systemColorMode = useSystemColorMode()
const resolvedColorMode = resolveColorMode(colorMode, systemColorMode)
const colorScheme = chooseColorScheme(resolvedColorMode, dayScheme, nightScheme)
const resolvedTheme = React.useMemo(() => applyColorScheme(theme, colorScheme), [theme, colorScheme])
const {resolvedTheme, resolvedColorScheme} = React.useMemo(
() => applyColorScheme(theme, colorScheme),
[theme, colorScheme]
)

// Update state if props change
React.useEffect(() => {
Expand All @@ -74,6 +78,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({children, ...props}
colorScheme,
colorMode,
resolvedColorMode,
resolvedColorScheme,
dayScheme,
nightScheme,
setColorMode,
Expand Down Expand Up @@ -156,9 +161,15 @@ function chooseColorScheme(colorMode: ColorMode, dayScheme: string, nightScheme:
}
}

function applyColorScheme(theme: Theme, colorScheme: string) {
function applyColorScheme(
theme: Theme,
colorScheme: string
): {resolvedTheme: Theme; resolvedColorScheme: string | undefined} {
if (!theme.colorSchemes) {
return theme
return {
resolvedTheme: theme,
resolvedColorScheme: undefined
}
}

if (!theme.colorSchemes[colorScheme]) {
Expand All @@ -167,10 +178,16 @@ function applyColorScheme(theme: Theme, colorScheme: string) {

// Apply the first defined color scheme
const defaultColorScheme = Object.keys(theme.colorSchemes)[0]
return deepmerge(theme, theme.colorSchemes[defaultColorScheme])
return {
resolvedTheme: deepmerge(theme, theme.colorSchemes[defaultColorScheme]),
resolvedColorScheme: defaultColorScheme
}
}

return deepmerge(theme, theme.colorSchemes[colorScheme])
return {
resolvedTheme: deepmerge(theme, theme.colorSchemes[colorScheme]),
resolvedColorScheme: colorScheme
}
}

export default ThemeProvider
116 changes: 116 additions & 0 deletions src/__tests__/ThemeProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -439,3 +439,119 @@ describe('useColorSchemeVar', () => {
expect(screen.getByText('Hello')).toHaveStyleRule('background-color', 'blue')
})
})

describe('useTheme().resolvedColorScheme', () => {
it('is undefined when not in a theme', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

render(<Component />)

expect(screen.getByTestId('text').textContent).toEqual('')
})

it('is undefined when the theme has no colorScheme object', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

render(
<ThemeProvider theme={{color: 'red'}}>
<Component />
</ThemeProvider>
)

expect(screen.getByTestId('text').textContent).toEqual('')
})

it('is the same as the applied colorScheme, when that colorScheme is in the theme', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

const schemeToApply = 'dark'

render(
<ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
<Component />
</ThemeProvider>
)

expect(exampleTheme.colorSchemes).toHaveProperty(schemeToApply)
expect(screen.getByTestId('text').textContent).toEqual(schemeToApply)
})

it('is the value of the fallback colorScheme applied when attempting to apply an invalid colorScheme', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

const schemeToApply = 'totally-invalid-colorscheme'
render(
<ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
<Component />
</ThemeProvider>
)

const defaultThemeColorScheme = Object.keys(exampleTheme.colorSchemes)[0]

expect(defaultThemeColorScheme).not.toEqual(schemeToApply)
expect(exampleTheme.colorSchemes).not.toHaveProperty(schemeToApply)
expect(screen.getByTestId('text').textContent).toEqual('light')
})

describe('nested theme', () => {
it('is the same as the applied colorScheme, when that colorScheme is in the theme', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

const schemeToApply = 'dark'

render(
<ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
<ThemeProvider>
<Component />
</ThemeProvider>
</ThemeProvider>
)

expect(exampleTheme.colorSchemes).toHaveProperty(schemeToApply)
expect(screen.getByTestId('text').textContent).toEqual(schemeToApply)
})

it('is the value of the fallback colorScheme applied when attempting to apply an invalid colorScheme', () => {
const Component = () => {
const {resolvedColorScheme} = useTheme()

return <Text data-testid="text">{resolvedColorScheme}</Text>
}

const schemeToApply = 'totally-invalid-colorscheme'
render(
<ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
<ThemeProvider>
<Component />
</ThemeProvider>
</ThemeProvider>
)

const defaultThemeColorScheme = Object.keys(exampleTheme.colorSchemes)[0]

expect(defaultThemeColorScheme).not.toEqual(schemeToApply)
expect(exampleTheme.colorSchemes).not.toHaveProperty(schemeToApply)
expect(screen.getByTestId('text').textContent).toEqual('light')
})
})
})

0 comments on commit e292877

Please sign in to comment.