snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
19
packages/storybook-dark-mode/CHANGELOG.md
Normal file
19
packages/storybook-dark-mode/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# @rocket.chat/storybook-dark-mode
|
||||
|
||||
## 4.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2060](https://github.com/RocketChat/fuselage/pull/2060) [`ee381bb`](https://github.com/RocketChat/fuselage/commit/ee381bb32982d0f4ed9ef26385b5016e0a2e4023) Thanks [@tassoevan](https://github.com/tassoevan)! - feat: Remove deprecations and keep documentation comments
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1999](https://github.com/RocketChat/fuselage/pull/1999) [`6e37f9d`](https://github.com/RocketChat/fuselage/commit/6e37f9d2fee4c9ebeb8ea2c1601bbd4c89905859) Thanks [@tassoevan](https://github.com/tassoevan)! - feat!: Upgrade to React 19
|
||||
|
||||
## 4.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1849](https://github.com/RocketChat/fuselage/pull/1849) [`99b319c`](https://github.com/RocketChat/fuselage/commit/99b319c6c2ef1015d1d1edb027068c23dc1e29d9) Thanks [@tassoevan](https://github.com/tassoevan)! - feat(storybook-dark-mode): Fork `storybook-dark-mode`
|
||||
20
packages/storybook-dark-mode/LICENSE
Normal file
20
packages/storybook-dark-mode/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2019 Andrew Lisowski <lisowski54@gmail.com>
|
||||
Copyright (c) 2026 Rocket.Chat Technologies Corp.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
51
packages/storybook-dark-mode/package.json
Normal file
51
packages/storybook-dark-mode/package.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "@rocket.chat/storybook-dark-mode",
|
||||
"version": "4.2.1",
|
||||
"description": "Toggle between light and dark mode in Storybook (Rocket.Chat fork)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RocketChat/Rocket.Chat.git",
|
||||
"directory": "packages/storybook-dark-mode"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Andrew Lisowski <lisowski54@gmail.com>",
|
||||
"contributors": [
|
||||
"Rocket.Chat Technologies Corp."
|
||||
],
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
"types": "dist/ts/index.d.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"preset.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rm -rf dist && tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json && tsc -p tsconfig.build.json",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/global": "~5.0.0",
|
||||
"@storybook/icons": "~1.6.0",
|
||||
"fast-deep-equal": "~3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/tsconfig": "workspace:~",
|
||||
"@types/react": "~19.2.18",
|
||||
"@types/react-dom": "~19.2.4",
|
||||
"eslint": "~9.39.5",
|
||||
"react": "~19.2.8",
|
||||
"react-dom": "~19.2.8",
|
||||
"storybook": "^9.1.20",
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*",
|
||||
"storybook": "^9.0.0"
|
||||
},
|
||||
"volta": {
|
||||
"extends": "../../package.json"
|
||||
}
|
||||
}
|
||||
7
packages/storybook-dark-mode/preset.js
Normal file
7
packages/storybook-dark-mode/preset.js
Normal file
@@ -0,0 +1,7 @@
|
||||
function managerEntries(entry = []) {
|
||||
return [...entry, require.resolve('./dist/esm/preset/manager')];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
managerEntries,
|
||||
};
|
||||
245
packages/storybook-dark-mode/src/Tool.tsx
Normal file
245
packages/storybook-dark-mode/src/Tool.tsx
Normal file
@@ -0,0 +1,245 @@
|
||||
import { global } from '@storybook/global';
|
||||
import { MoonIcon, SunIcon } from '@storybook/icons';
|
||||
import equal from 'fast-deep-equal';
|
||||
import * as React from 'react';
|
||||
import { IconButton } from 'storybook/internal/components';
|
||||
import { STORY_CHANGED, SET_STORIES, DOCS_RENDERED } from 'storybook/internal/core-events';
|
||||
import type { API } from 'storybook/manager-api';
|
||||
import { useParameter } from 'storybook/manager-api';
|
||||
import type { ThemeVars } from 'storybook/theming';
|
||||
import { themes } from 'storybook/theming';
|
||||
|
||||
import { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME } from './constants';
|
||||
|
||||
const { document, window } = global as { document: Document; window: Window };
|
||||
type Mode = 'light' | 'dark';
|
||||
|
||||
type DarkModeStore = {
|
||||
/** The class target in the preview iframe */
|
||||
classTarget: string;
|
||||
/** The current mode the storybook is set to */
|
||||
current: Mode;
|
||||
/** The dark theme for storybook */
|
||||
dark: ThemeVars;
|
||||
/** The dark class name for the preview iframe */
|
||||
darkClass: string | string[];
|
||||
/** The light theme for storybook */
|
||||
light: ThemeVars;
|
||||
/** The light class name for the preview iframe */
|
||||
lightClass: string | string[];
|
||||
/** Apply mode to iframe */
|
||||
stylePreview: boolean;
|
||||
/** Persist if the user has set the theme */
|
||||
userHasExplicitlySetTheTheme: boolean;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'sb-addon-themes-3';
|
||||
export const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
const defaultParams: Required<Omit<DarkModeStore, 'current'>> = {
|
||||
classTarget: 'body',
|
||||
dark: themes.dark,
|
||||
darkClass: ['dark'],
|
||||
light: themes.light,
|
||||
lightClass: ['light'],
|
||||
stylePreview: false,
|
||||
userHasExplicitlySetTheTheme: false,
|
||||
};
|
||||
|
||||
/** Persist the dark mode settings in localStorage */
|
||||
export const updateStore = (newStore: DarkModeStore) => {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
|
||||
};
|
||||
|
||||
/** Add the light/dark class to an element */
|
||||
const toggleDarkClass = (
|
||||
el: Element,
|
||||
{ current, darkClass = defaultParams.darkClass, lightClass = defaultParams.lightClass }: DarkModeStore,
|
||||
) => {
|
||||
if (current === 'dark') {
|
||||
el.classList.remove(...arrayify(lightClass));
|
||||
el.classList.add(...arrayify(darkClass));
|
||||
} else {
|
||||
el.classList.remove(...arrayify(darkClass));
|
||||
el.classList.add(...arrayify(lightClass));
|
||||
}
|
||||
};
|
||||
|
||||
/** Coerce a string to a single item array, or return an array as-is */
|
||||
const arrayify = (classes: string | string[]): string[] => {
|
||||
const arr: string[] = [];
|
||||
return arr.concat(classes).map((item) => item);
|
||||
};
|
||||
|
||||
/** Update the preview iframe class */
|
||||
const updatePreview = (store: DarkModeStore) => {
|
||||
const iframe = document.getElementById('storybook-preview-iframe') as HTMLIFrameElement;
|
||||
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
|
||||
const target = iframeDocument?.querySelector<HTMLElement>(store.classTarget);
|
||||
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleDarkClass(target, store);
|
||||
};
|
||||
|
||||
/** Update the manager iframe class */
|
||||
const updateManager = (store: DarkModeStore) => {
|
||||
const manager = document.querySelector(store.classTarget);
|
||||
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleDarkClass(manager, store);
|
||||
};
|
||||
|
||||
/** Update changed dark mode settings and persist to localStorage */
|
||||
export const store = (userTheme: Partial<DarkModeStore> = {}): DarkModeStore => {
|
||||
const storedItem = window.localStorage.getItem(STORAGE_KEY);
|
||||
|
||||
if (typeof storedItem === 'string') {
|
||||
const stored = JSON.parse(storedItem) as DarkModeStore;
|
||||
|
||||
if (userTheme) {
|
||||
if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
|
||||
stored.dark = userTheme.dark;
|
||||
updateStore(stored);
|
||||
}
|
||||
|
||||
if (userTheme.light && !equal(stored.light, userTheme.light)) {
|
||||
stored.light = userTheme.light;
|
||||
updateStore(stored);
|
||||
}
|
||||
}
|
||||
|
||||
return stored;
|
||||
}
|
||||
|
||||
return { ...defaultParams, ...userTheme } as DarkModeStore;
|
||||
};
|
||||
|
||||
// On initial load, set the dark mode class on the manager
|
||||
// This is needed if you're using mostly CSS overrides to styles the storybook
|
||||
// Otherwise the default theme is set in src/preset/manager.tsx
|
||||
updateManager(store());
|
||||
|
||||
type DarkModeProps = {
|
||||
/** The storybook API */
|
||||
api: API;
|
||||
};
|
||||
|
||||
/** A toolbar icon to toggle between dark and light themes in storybook */
|
||||
export function DarkMode({ api }: DarkModeProps) {
|
||||
const [isDark, setDark] = React.useState(prefersDark.matches);
|
||||
const darkModeParams = useParameter<Partial<DarkModeStore>>('darkMode', {});
|
||||
const { current: defaultMode, stylePreview, ...params } = darkModeParams;
|
||||
const channel = api.getChannel();
|
||||
// Save custom themes on init
|
||||
const userHasExplicitlySetTheTheme = React.useMemo(() => store(params).userHasExplicitlySetTheTheme, [params]);
|
||||
/** Set the theme in storybook, update the local state, and emit an event */
|
||||
const setMode = React.useCallback(
|
||||
(mode: Mode) => {
|
||||
const currentStore = store();
|
||||
api.setOptions({ theme: currentStore[mode] });
|
||||
setDark(mode === 'dark');
|
||||
api.getChannel()?.emit(DARK_MODE_EVENT_NAME, mode === 'dark');
|
||||
updateManager(currentStore);
|
||||
if (stylePreview) {
|
||||
updatePreview(currentStore);
|
||||
}
|
||||
},
|
||||
[api, stylePreview],
|
||||
);
|
||||
|
||||
/** Update the theme settings in localStorage, react, and storybook */
|
||||
const updateMode = React.useCallback(
|
||||
(mode?: Mode) => {
|
||||
const currentStore = store();
|
||||
const current = mode || (currentStore.current === 'dark' ? 'light' : 'dark');
|
||||
updateStore({ ...currentStore, current });
|
||||
setMode(current);
|
||||
},
|
||||
[setMode],
|
||||
);
|
||||
|
||||
/** Update the theme based on the color preference */
|
||||
function prefersDarkUpdate(event: MediaQueryListEvent) {
|
||||
if (userHasExplicitlySetTheTheme || defaultMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMode(event.matches ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
/** Render the current theme */
|
||||
const renderTheme = React.useCallback(() => {
|
||||
const { current = 'light' } = store();
|
||||
setMode(current);
|
||||
}, [setMode]);
|
||||
|
||||
/** Handle the user event and side effects */
|
||||
const handleIconClick = () => {
|
||||
updateMode();
|
||||
const currentStore = store();
|
||||
updateStore({ ...currentStore, userHasExplicitlySetTheTheme: true });
|
||||
};
|
||||
|
||||
/** When storybook params change update the stored themes */
|
||||
React.useEffect(() => {
|
||||
const currentStore = store();
|
||||
// Ensure we use the stores `current` value first to persist
|
||||
// themeing between page loads and story changes.
|
||||
updateStore({
|
||||
...currentStore,
|
||||
...darkModeParams,
|
||||
current: currentStore.current || darkModeParams.current,
|
||||
});
|
||||
renderTheme();
|
||||
}, [darkModeParams, renderTheme]);
|
||||
React.useEffect(() => {
|
||||
channel?.on(STORY_CHANGED, renderTheme);
|
||||
channel?.on(SET_STORIES, renderTheme);
|
||||
channel?.on(DOCS_RENDERED, renderTheme);
|
||||
prefersDark.addListener(prefersDarkUpdate);
|
||||
return () => {
|
||||
channel?.removeListener(STORY_CHANGED, renderTheme);
|
||||
channel?.removeListener(SET_STORIES, renderTheme);
|
||||
channel?.removeListener(DOCS_RENDERED, renderTheme);
|
||||
prefersDark.removeListener(prefersDarkUpdate);
|
||||
};
|
||||
});
|
||||
React.useEffect(() => {
|
||||
channel?.on(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
|
||||
return () => {
|
||||
channel?.removeListener(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
|
||||
};
|
||||
});
|
||||
// Storybook's first render doesn't have the global user params loaded so we
|
||||
// need the effect to run whenever defaultMode is updated
|
||||
React.useEffect(() => {
|
||||
// If a users has set the mode this is respected
|
||||
if (userHasExplicitlySetTheTheme) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultMode) {
|
||||
updateMode(defaultMode);
|
||||
} else if (prefersDark.matches) {
|
||||
updateMode('dark');
|
||||
}
|
||||
}, [defaultMode, updateMode, userHasExplicitlySetTheTheme]);
|
||||
return (
|
||||
<IconButton key='dark-mode' title={isDark ? 'Change theme to light mode' : 'Change theme to dark mode'} onClick={handleIconClick}>
|
||||
{isDark ? <SunIcon aria-hidden='true' /> : <MoonIcon aria-hidden='true' />}
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
export default DarkMode;
|
||||
2
packages/storybook-dark-mode/src/constants.ts
Normal file
2
packages/storybook-dark-mode/src/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DARK_MODE_EVENT_NAME = 'DARK_MODE';
|
||||
export const UPDATE_DARK_MODE_EVENT_NAME = 'UPDATE_DARK_MODE';
|
||||
21
packages/storybook-dark-mode/src/index.tsx
Normal file
21
packages/storybook-dark-mode/src/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { addons, useState, useEffect } from 'storybook/preview-api';
|
||||
|
||||
import { store } from './Tool';
|
||||
import { DARK_MODE_EVENT_NAME } from './constants';
|
||||
|
||||
/**
|
||||
* Returns the current state of storybook's dark-mode
|
||||
*/
|
||||
export function useDarkMode(): boolean {
|
||||
const [isDark, setIsDark] = useState(store().current === 'dark');
|
||||
|
||||
useEffect(() => {
|
||||
const chan = addons.getChannel();
|
||||
chan.on(DARK_MODE_EVENT_NAME, setIsDark);
|
||||
return () => chan.off(DARK_MODE_EVENT_NAME, setIsDark);
|
||||
}, []);
|
||||
|
||||
return isDark;
|
||||
}
|
||||
|
||||
export * from './constants';
|
||||
26
packages/storybook-dark-mode/src/preset/manager.tsx
Normal file
26
packages/storybook-dark-mode/src/preset/manager.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import * as React from 'react';
|
||||
import { Addon_TypesEnum } from 'storybook/internal/types';
|
||||
import { addons } from 'storybook/manager-api';
|
||||
import { themes } from 'storybook/theming';
|
||||
|
||||
import Tool, { prefersDark, store } from '../Tool';
|
||||
|
||||
const currentStore = store();
|
||||
const currentTheme = currentStore.current || (prefersDark.matches && 'dark') || 'light';
|
||||
|
||||
addons.setConfig({
|
||||
theme: {
|
||||
...themes[currentTheme],
|
||||
...currentStore[currentTheme],
|
||||
},
|
||||
});
|
||||
|
||||
addons.register('storybook/dark-mode', (api) => {
|
||||
addons.add('storybook/dark-mode', {
|
||||
title: 'dark mode',
|
||||
type: Addon_TypesEnum.TOOL,
|
||||
match: ({ viewMode }) => viewMode === 'story' || viewMode === 'docs',
|
||||
render: () => <Tool api={api} />,
|
||||
});
|
||||
});
|
||||
9
packages/storybook-dark-mode/tsconfig.build.json
Normal file
9
packages/storybook-dark-mode/tsconfig.build.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist/ts",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.spec.*", "**/*.stories*"]
|
||||
}
|
||||
12
packages/storybook-dark-mode/tsconfig.cjs.json
Normal file
12
packages/storybook-dark-mode/tsconfig.cjs.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./dist/cjs",
|
||||
"rootDir": "./src",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.spec.*", "**/*.stories*"]
|
||||
}
|
||||
12
packages/storybook-dark-mode/tsconfig.esm.json
Normal file
12
packages/storybook-dark-mode/tsconfig.esm.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "./dist/esm",
|
||||
"rootDir": "./src",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.spec.*", "**/*.stories*"]
|
||||
}
|
||||
7
packages/storybook-dark-mode/tsconfig.json
Normal file
7
packages/storybook-dark-mode/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "@rocket.chat/tsconfig/client.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user