Skip to content

Instantly share code, notes, and snippets.

@roginfarrer
Created October 2, 2025 20:26
Show Gist options
  • Save roginfarrer/af7fc08c660d7b4e2fd0adb9d4d0ed0f to your computer and use it in GitHub Desktop.
Save roginfarrer/af7fc08c660d7b4e2fd0adb9d4d0ed0f to your computer and use it in GitHub Desktop.
Local storage addons
import { addons, types, useGlobals } from "@storybook/manager-api";
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
import React, { useEffect, useRef } from "react";
import { Form, IconButton } from "@storybook/components";
import collageTheme from "collage/.storybook/themes/collage-theme";
const STORAGE_KEYS = {
ACTIVE_ROLE: "storybook_activeRole",
ACTIVE_FRAMEWORK: "storybook_activeFramework",
THEME: "storybook_theme",
};
const RoleTool = () => {
const [globals, updateGlobals] = useGlobals();
const selected = globals["activeRole"];
const hasSetInitialValue = useRef(false);
console.log(selected);
useEffect(() => {
if (!selected || hasSetInitialValue.current) return;
hasSetInitialValue.current = true;
const storedRole = localStorage.getItem(STORAGE_KEYS.ACTIVE_ROLE);
if (storedRole && storedRole !== selected) {
console.log("updating globals", storedRole);
updateGlobals({ activeRole: storedRole });
}
}, [selected]);
const handleRoleChange = (role: string) => {
localStorage.setItem(STORAGE_KEYS.ACTIVE_ROLE, role);
updateGlobals({ activeRole: role });
};
return (
<div
style={{
display: "flex",
gap: 4,
marginRight: 8,
marginLeft: 32,
}}
>
<IconButton
title="Designer"
onClick={() => handleRoleChange("designer")}
active={selected === globals["activeRole"]}
style={
selected === "designer"
? {
backgroundColor: collageTheme.barSelectedColor,
color: "white",
}
: { backgroundColor: collageTheme.barBg }
}
>
Designer
</IconButton>
<IconButton
title="Engineer"
onClick={() => handleRoleChange("developer")}
active={selected === globals["activeRole"]}
style={
selected === "developer"
? {
backgroundColor: collageTheme.barSelectedColor,
color: "white",
}
: {
backgroundColor: collageTheme.barBg,
}
}
>
Engineer
</IconButton>
</div>
);
};
const FrameworkTool = () => {
const [globals, updateGlobals] = useGlobals();
const selected = globals["activeFramework"];
useEffect(() => {
const storedFramework = localStorage.getItem(STORAGE_KEYS.ACTIVE_FRAMEWORK);
if (storedFramework && storedFramework !== selected) {
updateGlobals({ activeFramework: storedFramework });
}
}, []);
if (
window.location.href.includes("contribution-guidelines") ||
window.location.href.includes("tools") ||
window.location.href.includes("welcome") ||
window.location.href.includes("foundations") ||
window.location.href.includes("what-s")
) {
return null;
}
if (globals.activeRole === "developer") {
return (
<div style={{ margin: "auto" }}>
<Form.Select
id="platformSelect"
className="platform-select"
onChange={(e) => {
localStorage.setItem(
STORAGE_KEYS.ACTIVE_FRAMEWORK,
e.target.value
);
updateGlobals({
activeFramework: e.target.value,
});
}}
value={selected}
aria-label="Framework"
>
<optgroup label="Framework">
<option key="preact" value="preact">
Preact
</option>
<option key="mustache" value="mustache">
Mustache
</option>
</optgroup>
</Form.Select>
</div>
);
}
return null;
};
const ThemeTool = () => {
const [globals, updateGlobals] = useGlobals();
const selected = globals["theme"];
useEffect(() => {
const storedTheme = localStorage.getItem(STORAGE_KEYS.THEME);
if (storedTheme && storedTheme !== selected) {
updateGlobals({ theme: storedTheme });
}
}, []);
return (
<div style={{ margin: "auto" }}>
<Form.Select
id="themeSelect"
className="theme-select"
onChange={(e) => {
const theme =
e.target.value === "Refresh" ? "Refresh" : "Legacy";
localStorage.setItem(STORAGE_KEYS.THEME, theme);
updateGlobals({ theme });
}}
value={selected}
aria-label="Theme"
>
<optgroup label="Theme">
<option key="Refresh" value="Refresh">
Refresh
</option>
<option key="Legacy" value="Legacy">
Legacy
</option>
</optgroup>
</Form.Select>
</div>
);
};
// Functionality commented out until design provides UI for mobile views
// const ViewportTool = () => {
// const [globals, updateGlobals] = useGlobals();
// const selected = globals["activeViewport"];
// return (
// <div style={{ margin: "auto", marginLeft: 8 }}>
// <Form.Select
// id="viewPortSelect"
// className="viewport-select"
// aria-label="Viewport"
// onChange={(e) => {
// updateGlobals({
// activeViewport: e.target.value,
// });
// }}
// value={selected}
// >
// <optgroup label="Viewport">
// <option key="mobile" value="mobile">
// Mobile
// </option>
// <option key="desktop" value="desktop">
// Desktop
// </option>
// </optgroup>
// </Form.Select>
// </div>
// );
// };
addons.register("custom/multi-switcher-addon", () => {
addons.add("custom/role-tool", {
title: "View Mode",
type: types.TOOL,
match: () => true,
render: () => {
return <RoleTool />;
},
});
addons.add("custom/theme-tool", {
title: "Theme",
type: types.TOOL,
match: () => true,
render: () => {
return (
<div style={{ marginLeft: 16, display: "flex" }}>
<ThemeTool />
</div>
);
},
});
addons.add("custom/platform-tool", {
title: "Platform",
type: types.TOOL,
match: () => true,
render: () => {
return (
<div style={{ marginLeft: 8, display: "flex" }}>
<FrameworkTool />
</div>
);
},
});
// addons.add("custom/viewport-tool", {
// title: "Viewport",
// type: types.TOOL,
// match: () => true,
// render: () => {
// return <ViewportTool />;
// },
// });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment