mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
feat(storybook): preview app (#3765)
This commit is contained in:
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -147,10 +147,13 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
electron-install: false
|
electron-install: false
|
||||||
- run: yarn chromatic
|
- name: Publish to Chromatic
|
||||||
working-directory: apps/storybook
|
uses: chromaui/action@v1
|
||||||
env:
|
with:
|
||||||
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
workingDir: apps/storybook
|
||||||
|
buildScriptName: build
|
||||||
|
onlyStoryNames: Preview/Core
|
||||||
|
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
|
||||||
build-core:
|
build-core:
|
||||||
name: Build @affine/core
|
name: Build @affine/core
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
"dev": "yarn -T run dev-core",
|
"dev": "yarn -T run dev-core",
|
||||||
"static-server": "ts-node-esm ./server.mts"
|
"static-server": "ts-node-esm ./server.mts"
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
"./app": "./src/app.tsx",
|
||||||
|
"./router": "./src/router.ts",
|
||||||
|
"./bootstrap/setup": "./src/bootstrap/setup.ts"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine-test/fixtures": "workspace:*",
|
"@affine-test/fixtures": "workspace:*",
|
||||||
"@affine/component": "workspace:*",
|
"@affine/component": "workspace:*",
|
||||||
|
|||||||
@@ -169,7 +169,14 @@ function createFirstAppData() {
|
|||||||
rootStore.set(rootWorkspacesMetadataAtom, result);
|
rootStore.set(rootWorkspacesMetadataAtom, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isSetup = false;
|
||||||
|
|
||||||
export async function setup() {
|
export async function setup() {
|
||||||
|
if (isSetup) {
|
||||||
|
console.warn('already setup');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isSetup = true;
|
||||||
rootStore.set(
|
rootStore.set(
|
||||||
workspaceAdaptersAtom,
|
workspaceAdaptersAtom,
|
||||||
WorkspaceAdapters as Record<
|
WorkspaceAdapters as Record<
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import type { HTMLAttributes } from 'react';
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { cloneElement, useState } from 'react';
|
import { cloneElement, useState } from 'react';
|
||||||
|
|
||||||
|
import edgelessHover from './animation-data/edgeless-hover.json';
|
||||||
|
import pageHover from './animation-data/page-hover.json';
|
||||||
import { StyledSwitchItem } from './style';
|
import { StyledSwitchItem } from './style';
|
||||||
|
|
||||||
type HoverAnimateControllerProps = {
|
type HoverAnimateControllerProps = {
|
||||||
@@ -52,7 +54,7 @@ export const PageSwitchItem = (
|
|||||||
options={{
|
options={{
|
||||||
loop: false,
|
loop: false,
|
||||||
autoplay: false,
|
autoplay: false,
|
||||||
animationData: require('./animation-data/page-hover.json'),
|
animationData: pageHover,
|
||||||
rendererSettings: {
|
rendererSettings: {
|
||||||
preserveAspectRatio: 'xMidYMid slice',
|
preserveAspectRatio: 'xMidYMid slice',
|
||||||
},
|
},
|
||||||
@@ -71,7 +73,7 @@ export const EdgelessSwitchItem = (
|
|||||||
options={{
|
options={{
|
||||||
loop: false,
|
loop: false,
|
||||||
autoplay: false,
|
autoplay: false,
|
||||||
animationData: require('./animation-data/edgeless-hover.json'),
|
animationData: edgelessHover,
|
||||||
rendererSettings: {
|
rendererSettings: {
|
||||||
preserveAspectRatio: 'xMidYMid slice',
|
preserveAspectRatio: 'xMidYMid slice',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
|
import type { RouteObject } from 'react-router-dom';
|
||||||
import { createBrowserRouter } from 'react-router-dom';
|
import { createBrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
export const router = createBrowserRouter(
|
export const routes = [
|
||||||
[
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
lazy: () => import('./pages/index'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/workspace/:workspaceId',
|
|
||||||
lazy: () => import('./pages/workspace/index'),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'all',
|
|
||||||
lazy: () => import('./pages/workspace/all-page'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'trash',
|
|
||||||
lazy: () => import('./pages/workspace/trash-page'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':pageId',
|
|
||||||
lazy: () => import('./pages/workspace/detail-page'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/404',
|
|
||||||
lazy: () => import('./pages/404'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '*',
|
|
||||||
lazy: () => import('./pages/404'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
{
|
||||||
future: {
|
path: '/',
|
||||||
v7_normalizeFormMethod: true,
|
lazy: () => import('./pages/index'),
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
);
|
path: '/workspace/:workspaceId',
|
||||||
|
lazy: () => import('./pages/workspace/index'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'all',
|
||||||
|
lazy: () => import('./pages/workspace/all-page'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'trash',
|
||||||
|
lazy: () => import('./pages/workspace/trash-page'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':pageId',
|
||||||
|
lazy: () => import('./pages/workspace/detail-page'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
lazy: () => import('./pages/404'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
lazy: () => import('./pages/404'),
|
||||||
|
},
|
||||||
|
] satisfies [RouteObject, ...RouteObject[]];
|
||||||
|
|
||||||
|
export const router = createBrowserRouter(routes, {
|
||||||
|
future: {
|
||||||
|
v7_normalizeFormMethod: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"typeRoots": ["../../node_modules", "../../node_modules/@types"],
|
"typeRoots": ["../../node_modules", "../../node_modules/@types"],
|
||||||
"types": ["webpack-env", "ses", "affine__env"]
|
"types": ["webpack-env", "ses", "affine__env"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json"],
|
||||||
"exclude": ["node_modules"],
|
"exclude": ["node_modules"],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export default {
|
|||||||
'@storybook/addon-interactions',
|
'@storybook/addon-interactions',
|
||||||
'@storybook/addon-storysource',
|
'@storybook/addon-storysource',
|
||||||
'storybook-dark-mode',
|
'storybook-dark-mode',
|
||||||
|
'storybook-addon-react-router-v6',
|
||||||
],
|
],
|
||||||
framework: {
|
framework: {
|
||||||
name: '@storybook/react-vite',
|
name: '@storybook/react-vite',
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import '@affine/component/theme/global.css';
|
import '@affine/component/theme/global.css';
|
||||||
import '@affine/component/theme/theme.css';
|
import '@affine/component/theme/theme.css';
|
||||||
import { LOCALES, createI18n } from '@affine/i18n';
|
import '@toeverything/components/style.css';
|
||||||
|
import { createI18n } from '@affine/i18n';
|
||||||
import { ThemeProvider, useTheme } from 'next-themes';
|
import { ThemeProvider, useTheme } from 'next-themes';
|
||||||
import { setupGlobal } from '@affine/env/global';
|
|
||||||
import type { ComponentType } from 'react';
|
import type { ComponentType } from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useDarkMode } from 'storybook-dark-mode';
|
import { useDarkMode } from 'storybook-dark-mode';
|
||||||
|
import { setup } from '@affine/core/bootstrap/setup';
|
||||||
|
import { AffineContext } from '@affine/component/context';
|
||||||
|
import { use } from 'foxact/use';
|
||||||
|
|
||||||
setupGlobal();
|
const setupPromise = setup();
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
backgrounds: { disable: true },
|
backgrounds: { disable: true },
|
||||||
@@ -20,22 +23,6 @@ export const parameters = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const globalTypes = {
|
|
||||||
locale: {
|
|
||||||
name: 'Locale',
|
|
||||||
description: 'Internationalization locale',
|
|
||||||
defaultValue: 'en',
|
|
||||||
toolbar: {
|
|
||||||
icon: 'globe',
|
|
||||||
items: LOCALES.map(locale => ({
|
|
||||||
title: locale.originalName,
|
|
||||||
value: locale.tag,
|
|
||||||
right: locale.flagEmoji,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createI18nDecorator = () => {
|
const createI18nDecorator = () => {
|
||||||
const i18n = createI18n();
|
const i18n = createI18n();
|
||||||
const withI18n = (Story: any, context: any) => {
|
const withI18n = (Story: any, context: any) => {
|
||||||
@@ -59,10 +46,13 @@ const Component = () => {
|
|||||||
|
|
||||||
export const decorators = [
|
export const decorators = [
|
||||||
(Story: ComponentType) => {
|
(Story: ComponentType) => {
|
||||||
|
use(setupPromise);
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<Component />
|
<AffineContext>
|
||||||
<Story />
|
<Component />
|
||||||
|
<Story />
|
||||||
|
</AffineContext>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "storybook dev -p 6006",
|
"dev": "storybook dev -p 6006",
|
||||||
"build": "storybook build",
|
"build": "storybook build",
|
||||||
"test": "test-storybook",
|
"test": "test-storybook"
|
||||||
"chromatic": "npx chromatic --build-script-name build"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/component": "workspace:*",
|
"@affine/component": "workspace:*",
|
||||||
@@ -40,7 +39,8 @@
|
|||||||
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
|
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
|
||||||
"chromatic": "^6.22.0",
|
"chromatic": "^6.22.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0"
|
"react-dom": "18.2.0",
|
||||||
|
"storybook-addon-react-router-v6": "^2.0.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@blocksuite/blocks": "*",
|
"@blocksuite/blocks": "*",
|
||||||
|
|||||||
55
apps/storybook/src/stories/core.stories.tsx
Normal file
55
apps/storybook/src/stories/core.stories.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { routes } from '@affine/core/router';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
import type { StoryContext, StoryFn } from '@storybook/react';
|
||||||
|
import { userEvent } from '@storybook/testing-library';
|
||||||
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
reactRouterOutlets,
|
||||||
|
reactRouterParameters,
|
||||||
|
withRouter,
|
||||||
|
} from 'storybook-addon-react-router-v6';
|
||||||
|
|
||||||
|
const withCleanLocalStorage = (Story: StoryFn, context: StoryContext) => {
|
||||||
|
localStorage.clear();
|
||||||
|
return <Story {...context.args} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FakeApp = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
// fixme: `key` is a hack to force the storybook to re-render the outlet
|
||||||
|
return <Outlet key={location.pathname} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Preview/Core',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Index: StoryFn = () => {
|
||||||
|
return <FakeApp />;
|
||||||
|
};
|
||||||
|
|
||||||
|
Index.decorators = [withRouter, withCleanLocalStorage];
|
||||||
|
Index.parameters = {
|
||||||
|
reactRouter: reactRouterParameters({
|
||||||
|
routing: reactRouterOutlets(routes),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingPage: StoryFn = () => {
|
||||||
|
return <FakeApp />;
|
||||||
|
};
|
||||||
|
|
||||||
|
SettingPage.play = async ({ canvasElement }) => {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
const settingModalBtn = canvasElement.querySelector(
|
||||||
|
'[data-testid="settings-modal-trigger"]'
|
||||||
|
) as Element;
|
||||||
|
expect(settingModalBtn).not.toBeNull();
|
||||||
|
await userEvent.click(settingModalBtn);
|
||||||
|
};
|
||||||
|
SettingPage.decorators = [withRouter, withCleanLocalStorage];
|
||||||
|
SettingPage.parameters = {
|
||||||
|
reactRouter: reactRouterParameters({
|
||||||
|
routing: reactRouterOutlets(routes),
|
||||||
|
}),
|
||||||
|
};
|
||||||
@@ -34,7 +34,7 @@ AffineOperationCell.play = async ({ canvasElement }) => {
|
|||||||
'[data-testid="page-list-operation-button"]'
|
'[data-testid="page-list-operation-button"]'
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
expect(button).not.toBeNull();
|
expect(button).not.toBeNull();
|
||||||
userEvent.click(button);
|
await userEvent.click(button);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ AffineNewPageButton.play = async ({ canvasElement }) => {
|
|||||||
expect(button).not.toBeNull();
|
expect(button).not.toBeNull();
|
||||||
const dropdown = button.querySelector('svg') as SVGSVGElement;
|
const dropdown = button.querySelector('svg') as SVGSVGElement;
|
||||||
expect(dropdown).not.toBeNull();
|
expect(dropdown).not.toBeNull();
|
||||||
userEvent.click(dropdown);
|
await userEvent.click(dropdown);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AffineAllPageList: StoryFn<typeof PageList> = ({ ...props }) => (
|
export const AffineAllPageList: StoryFn<typeof PageList> = ({ ...props }) => (
|
||||||
@@ -69,11 +69,11 @@ AffineAllPageList.args = {
|
|||||||
favorite: false,
|
favorite: false,
|
||||||
icon: <PageIcon />,
|
icon: <PageIcon />,
|
||||||
isPublicPage: true,
|
isPublicPage: true,
|
||||||
title: 'Today Page',
|
title: 'Last Page',
|
||||||
tags: [],
|
tags: [],
|
||||||
preview: 'this is page preview',
|
preview: 'this is page preview',
|
||||||
createDate: new Date(),
|
createDate: new Date('2021-01-01'),
|
||||||
updatedDate: new Date(),
|
updatedDate: new Date('2023-08-15'),
|
||||||
bookmarkPage: () => toast('Bookmark page'),
|
bookmarkPage: () => toast('Bookmark page'),
|
||||||
onClickPage: () => toast('Click page'),
|
onClickPage: () => toast('Click page'),
|
||||||
onDisablePublicSharing: () => toast('Disable public sharing'),
|
onDisablePublicSharing: () => toast('Disable public sharing'),
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../../apps/core"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "../../packages/component"
|
"path": "../../packages/component"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"composite": true,
|
"composite": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "bundler",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"noEmit": false,
|
"noEmit": false,
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"include": [".storybook/**/*"],
|
"include": [".storybook/**/*"],
|
||||||
"exclude": ["lib"],
|
"exclude": ["lib"],
|
||||||
"references": [
|
"references": [
|
||||||
|
{ "path": "../../apps/core" },
|
||||||
{ "path": "../../packages/i18n" },
|
{ "path": "../../packages/i18n" },
|
||||||
{
|
{
|
||||||
"path": "../../packages/env"
|
"path": "../../packages/env"
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
"skipLibCheck": true, // skip all type checks for .d.ts files
|
"skipLibCheck": true, // skip all type checks for .d.ts files
|
||||||
|
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@affine/core/*": ["./packages/core/src/*"],
|
||||||
"@affine/cli/*": ["./packages/cli/src/*"],
|
"@affine/cli/*": ["./packages/cli/src/*"],
|
||||||
"@affine/component": ["./packages/component/src/index"],
|
"@affine/component": ["./packages/component/src/index"],
|
||||||
"@affine/component/*": [
|
"@affine/component/*": [
|
||||||
|
|||||||
37
yarn.lock
37
yarn.lock
@@ -709,6 +709,7 @@ __metadata:
|
|||||||
react-dom: 18.2.0
|
react-dom: 18.2.0
|
||||||
serve: ^14.2.0
|
serve: ^14.2.0
|
||||||
storybook: ^7.2.3
|
storybook: ^7.2.3
|
||||||
|
storybook-addon-react-router-v6: ^2.0.4
|
||||||
storybook-dark-mode: ^3.0.1
|
storybook-dark-mode: ^3.0.1
|
||||||
wait-on: ^7.0.1
|
wait-on: ^7.0.1
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -15874,6 +15875,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"compare-versions@npm:^6.0.0":
|
||||||
|
version: 6.1.0
|
||||||
|
resolution: "compare-versions@npm:6.1.0"
|
||||||
|
checksum: d4e2a45706a023d8d0b6680338b66b79e20bd02d1947f0ac6531dab634cbed89fa373b3f03d503c5e489761194258d6e1bae67a07f88b1efc61648454f2d47e7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"component-emitter@npm:^1.3.0":
|
"component-emitter@npm:^1.3.0":
|
||||||
version: 1.3.0
|
version: 1.3.0
|
||||||
resolution: "component-emitter@npm:1.3.0"
|
resolution: "component-emitter@npm:1.3.0"
|
||||||
@@ -27724,7 +27732,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-inspector@npm:^6.0.0":
|
"react-inspector@npm:6.0.2, react-inspector@npm:^6.0.0":
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
resolution: "react-inspector@npm:6.0.2"
|
resolution: "react-inspector@npm:6.0.2"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -29775,6 +29783,33 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"storybook-addon-react-router-v6@npm:^2.0.4":
|
||||||
|
version: 2.0.4
|
||||||
|
resolution: "storybook-addon-react-router-v6@npm:2.0.4"
|
||||||
|
dependencies:
|
||||||
|
compare-versions: ^6.0.0
|
||||||
|
react-inspector: 6.0.2
|
||||||
|
peerDependencies:
|
||||||
|
"@storybook/blocks": ^7.0.0
|
||||||
|
"@storybook/channels": ^7.0.0
|
||||||
|
"@storybook/components": ^7.0.0
|
||||||
|
"@storybook/core-events": ^7.0.0
|
||||||
|
"@storybook/manager-api": ^7.0.0
|
||||||
|
"@storybook/preview-api": ^7.0.0
|
||||||
|
"@storybook/theming": ^7.0.0
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react-router: ^6.4.0
|
||||||
|
react-router-dom: ^6.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
checksum: 2920eff2f15b84bc57c0260286d656373130dfdd25c4299319da94e427e2dcf45bf8a73221b47818d470e029d2588d60d498b0163b50b7d6db17253ffa36d11f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"storybook-dark-mode@npm:^3.0.1":
|
"storybook-dark-mode@npm:^3.0.1":
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
resolution: "storybook-dark-mode@npm:3.0.1"
|
resolution: "storybook-dark-mode@npm:3.0.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user