Skip to content
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
15 changes: 15 additions & 0 deletions .storybook/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,18 @@ addons.setConfig({
},
theme: theme,
});

// Hide "Reset selection" option from the theme toolbar dropdown
if (typeof window !== "undefined") {
const hideResetSelection = () => {
const resetOption = document.querySelector(
'ul[role="listbox"][aria-label="Global theme for components"] > li[role="option"][id$="-opt-undefined"]'
);
if (resetOption) {
(resetOption as HTMLElement).style.display = "none";
}
};

const observer = new MutationObserver(hideResetSelection);
observer.observe(document.body, { childList: true, subtree: true });
}
61 changes: 47 additions & 14 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState, useEffect, ReactNode } from "react";
import type { Preview } from "@storybook/react-vite";
import { Decorator } from "@storybook/react-vite";
import { styled } from "styled-components";
Expand All @@ -24,32 +24,65 @@ export const globalTypes = {
theme: {
name: "Theme",
description: "Global theme for components",
defaultValue: "dark",
defaultValue: "system",
toolbar: {
// The icon for the toolbar item
icon: "circlehollow",
// Array of options
items: [
{ value: "system", icon: "browser", title: "system" },
{ value: "dark", icon: "moon", title: "dark" },
{ value: "light", icon: "sun", title: "light" },
],
// Property that specifies if the name of the item will be displayed
showName: true,
},
},
};

const getSystemTheme = (): "dark" | "light" => {
if (typeof window !== "undefined" && window.matchMedia) {
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
return "dark";
};

interface ThemeWrapperProps {
themeSelection: string | undefined;
children: ReactNode;
}

const ThemeWrapper = ({ themeSelection, children }: ThemeWrapperProps) => {
const [systemTheme, setSystemTheme] = useState<"dark" | "light">(getSystemTheme);

// Listen for system theme changes
useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = () => {
setSystemTheme(mediaQuery.matches ? "dark" : "light");
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, []);

// Resolve the actual theme: handle "system" and fallback for undefined/null
const theme =
themeSelection === "system" || !themeSelection ? systemTheme : themeSelection;

return (
<ClickUIProvider theme={theme} config={{ tooltip: { delayDuration: 0 } }}>
<ThemeBlock $left>{children}</ThemeBlock>
</ClickUIProvider>
);
};

const withTheme: Decorator = (StoryFn, context) => {
const parameters = context.parameters;
const theme = parameters?.theme || context.globals.theme;
const themeSelection = parameters?.theme || context.globals.theme;

return (
<ClickUIProvider
theme={theme}
config={{ tooltip: { delayDuration: 0 } }}
>
<ThemeBlock $left>
<StoryFn />
</ThemeBlock>
</ClickUIProvider>
<ThemeWrapper themeSelection={themeSelection}>
<StoryFn />
</ThemeWrapper>
);
};

Expand Down