Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WB-1634: Add supported themes list to wb-theming #2098

Merged
merged 2 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/nine-meals-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@khanacademy/wonder-blocks-theming": minor
"@khanacademy/wonder-blocks-button": patch
"@khanacademy/wonder-blocks-switch": patch
---

Export supported themes list, change WB components to use these new exports
13 changes: 7 additions & 6 deletions __docs__/wonder-blocks-theming/adding-a-theme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,31 +107,32 @@ to access the theme values in the component styles.
import {
createThemeContext,
ThemeSwitcherContext,
Themes,
} from "@khanacademy/wonder-blocks-theming";

import defaultTheme from "./default";
import brandTheme from "./brand";

// Infer the type of the theme from the default theme.
// NOTE: Any other theme created should be compatible with this type.
export type ButtonThemeContract = typeof defaultTheme;

// Define the themes that will be available to the consumer(s).
const themes = {
const themes: Themes<ButtonThemeContract> = {
default: defaultTheme,
brand: brandTheme,
};

// Create the theme context and assign an initial value.
export const ButtonThemeContext = createThemeContext(themes.default);

// Infer the type of the theme from the default theme.
// NOTE: Any other theme created should be compatible with this type.
export type ButtonThemeContract = typeof defaultTheme;

type Props = {
children: React.ReactNode;
};

export default function ThemedButton({children}: Props) {
const currentTheme = React.useContext(ThemeSwitcherContext);
const theme = themes[currentTheme as keyof typeof themes] ?? themes.default;
const theme = themes[currentTheme] ?? themes.default;

return (
<ButtonThemeContext.Provider value={theme}>
Expand Down
13 changes: 7 additions & 6 deletions __docs__/wonder-blocks-theming/theme-examples.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";

import {StyleSheet} from "aphrodite";
import Button from "@khanacademy/wonder-blocks-button";
import {View} from "@khanacademy/wonder-blocks-core";
import {
createThemeContext,
Expand All @@ -12,6 +13,7 @@ import {
ThemeSwitcherContext,
withScopedTheme,
WithThemeProps,
SupportedThemes,
} from "@khanacademy/wonder-blocks-theming";

const defaultTheme = {
Expand Down Expand Up @@ -41,9 +43,8 @@ const customTheme = mergeTheme(defaultTheme, {

const ThemeContext = createThemeContext(defaultTheme);

// TODO(WB-1577): Replace this with the actual WB Button component.
type ButtonProps = {
children?: React.ReactNode;
children?: string;
onClick?: (e: React.SyntheticEvent) => unknown;
};

Expand All @@ -54,7 +55,7 @@ const ThemedButton = ({
const {theme} = useScopedTheme(ThemeContext);

return (
<button
<Button
style={{
background: theme.color.bg.primary,
color: theme.color.text.light,
Expand All @@ -64,7 +65,7 @@ const ThemedButton = ({
onClick={onClick}
>
{children}
</button>
</Button>
);
};

Expand Down Expand Up @@ -197,10 +198,10 @@ function ThemedButtonContainer(props: ButtonProps) {
}

export const ThemeSwitcherContextExample = () => {
const [theme, setTheme] = React.useState("default");
const [theme, setTheme] = React.useState<SupportedThemes>("default");

const changeTheme = () => {
const newTheme = theme === "custom" ? "default" : "custom";
const newTheme = theme === "khanmigo" ? "default" : "khanmigo";
setTheme(newTheme);
};

Expand Down
7 changes: 5 additions & 2 deletions __docs__/wonder-blocks-theming/theme-switcher-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ const ThemeSwitcherContext: React.Context<string>;
<ThemeSwitcherContextExample />

```ts
import {ThemeSwitcherContext} from "@khanacademy/wonder-blocks-theming";
import {
SupportedThemes,
ThemeSwitcherContext,
} from "@khanacademy/wonder-blocks-theming";

export default function App() {
const [theme, setTheme] = React.useState("default");
const [theme, setTheme] = React.useState<SupportedThemes>("default");

const changeTheme = () => {
const newTheme = theme === "brand" ? "default" : "brand";
Expand Down
9 changes: 5 additions & 4 deletions packages/wonder-blocks-button/src/themes/themed-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import {
createThemeContext,
Themes,
ThemeSwitcherContext,
} from "@khanacademy/wonder-blocks-theming";

Expand All @@ -11,16 +12,16 @@ type Props = {
children: React.ReactNode;
};

export type ButtonThemeContract = typeof defaultTheme;

/**
* The themes available to the Button component.
*/
const themes = {
const themes: Themes<ButtonThemeContract> = {
default: defaultTheme,
khanmigo: khanmigoTheme,
};

export type ButtonThemeContract = typeof defaultTheme;

/**
* The context that provides the theme to the Button component.
* This is generally consumed via the `useScopedTheme` hook.
Expand All @@ -33,7 +34,7 @@ export const ButtonThemeContext = createThemeContext(defaultTheme);
export default function ThemedButton(props: Props) {
const currentTheme = React.useContext(ThemeSwitcherContext);

const theme = themes[currentTheme as keyof typeof themes] || defaultTheme;
const theme = themes[currentTheme] || defaultTheme;
return (
<ButtonThemeContext.Provider value={theme}>
{props.children}
Expand Down
9 changes: 5 additions & 4 deletions packages/wonder-blocks-switch/src/themes/themed-switch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import {
createThemeContext,
Themes,
ThemeSwitcherContext,
} from "@khanacademy/wonder-blocks-theming";

Expand All @@ -11,16 +12,16 @@ type Props = {
children: React.ReactNode;
};

export type SwitchThemeContract = typeof defaultTheme;

/**
* The themes available to the Switch component.
*/
const themes = {
const themes: Themes<SwitchThemeContract> = {
default: defaultTheme,
khanmigo: khanmigoTheme,
};

export type SwitchThemeContract = typeof defaultTheme;

/**
* The context that provides the theme to the Switch component.
* This is generally consumed via the `useScopedTheme` hook.
Expand All @@ -33,7 +34,7 @@ export const SwitchThemeContext = createThemeContext(defaultTheme);
export default function ThemedSwitch(props: Props) {
const currentTheme = React.useContext(ThemeSwitcherContext);

const theme = themes[currentTheme as keyof typeof themes] || defaultTheme;
const theme = themes[currentTheme] || defaultTheme;
return (
<SwitchThemeContext.Provider value={theme}>
{props.children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type WithoutTheme<T> = Omit<T, keyof WithThemeProps>;
* A higher order component that includes the themed styles in the props of the
* wrapped component as `wbThemeStyles`.
*/
export default function withScopedTheme<T>(
export default function withScopedTheme<T extends object>(
styleSheet: ThemedStylesFn<T>,
themeContext: React.Context<T>,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type ScopedTheme<T> = {
themeName: string;
};

export default function useScopedTheme<T>(
export default function useScopedTheme<T extends object>(
themeContext: React.Context<T>,
): ScopedTheme<T> {
const theme = React.useContext(themeContext);
Expand Down
2 changes: 1 addition & 1 deletion packages/wonder-blocks-theming/src/hooks/use-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {ThemedStylesFn} from "../types";
* @param theme The theme to be passed to the styles.
* @returns The styleSheet object.
*/
export default function useStyles<T>(
export default function useStyles<T extends object>(
styles: ThemedStylesFn<T>,
theme: T,
): StyleDeclaration {
Expand Down
2 changes: 1 addition & 1 deletion packages/wonder-blocks-theming/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export {
default as withScopedTheme,
type WithThemeProps,
} from "./components/with-scoped-theme";
export {type ThemedStylesFn} from "./types";
export {type ThemedStylesFn, type SupportedThemes, type Themes} from "./types";
export {default as useStyles} from "./hooks/use-styles";
export {ThemeSwitcherContext} from "./utils/theme-switcher-context";
5 changes: 4 additions & 1 deletion packages/wonder-blocks-theming/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import {StyleDeclaration} from "aphrodite";

export type ThemedStylesFn<T> = (theme: T) => StyleDeclaration;
export type ThemedStylesFn<T extends object> = (theme: T) => StyleDeclaration;

export type SupportedThemes = "default" | "khanmigo";
export type Themes<T extends object> = Partial<Record<SupportedThemes, T>>;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("ThemeSwitcherContext", () => {

// Act
render(
<ThemeSwitcherContext.Provider value="dark">
<ThemeSwitcherContext.Provider value="khanmigo">
<ThemeSwitcherContext.Consumer>
{(value) => <>The current theme is: {value}</>}
</ThemeSwitcherContext.Consumer>
Expand All @@ -32,6 +32,6 @@ describe("ThemeSwitcherContext", () => {
);

// Assert
expect(screen.getByText(/The current theme is: dark/)).toBeTruthy();
expect(screen.getByText(/The current theme is: khanmigo/)).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import {SupportedThemes} from "../types";

/**
* A React Context that holds a reference to the selected theme. It should use
Expand All @@ -8,4 +9,5 @@ import * as React from "react";
* @param theme The theme name to be used. It should be one of the themes
* defined in the themes object. Defaults to `default`.
*/
export const ThemeSwitcherContext = React.createContext<string>("default");
export const ThemeSwitcherContext =
React.createContext<SupportedThemes>("default");
Loading