fix(storybook): remove storybook testing (#6595)

remove tests/storybook

@affine/components storybook still exists
This commit is contained in:
EYHN
2024-04-17 17:27:33 +08:00
committed by GitHub
parent ed96c4ece4
commit 4b933466f4
38 changed files with 8 additions and 2593 deletions

View File

@@ -54,7 +54,6 @@ const allPackages = [
'packages/common/theme',
'packages/common/y-indexeddb',
'tools/cli',
'tests/storybook',
];
/**

View File

@@ -1,51 +0,0 @@
name: Publish Storybook
env:
NODE_OPTIONS: --max-old-space-size=4096
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
branches:
- canary
paths-ignore:
- README.md
- .github/**
- packages/backend/server
- packages/frontend/electron
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-storybook:
name: Publish Storybook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- uses: chromaui/action-next@v1
with:
workingDir: tests/storybook
buildScriptName: build
exitOnceUploaded: true
onlyChanged: false
diagnostics: true
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}
path: |
chromatic-diagnostics.json
**/build-storybook.log

View File

@@ -1,51 +0,0 @@
name: Publish UI Storybook
env:
NODE_OPTIONS: --max-old-space-size=4096
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
branches:
- canary
paths-ignore:
- README.md
- .github/**
- packages/backend/server
- packages/frontend/electron
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-ui-storybook:
name: Publish UI Storybook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- uses: chromaui/action-next@v1
with:
workingDir: packages/frontend/component
buildScriptName: build:storybook
exitOnceUploaded: true
onlyChanged: false
diagnostics: true
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_UI_PROJECT_TOKEN }}
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}
path: |
chromatic-diagnostics.json
**/build-storybook.log

View File

@@ -112,7 +112,7 @@ If you have questions, you are welcome to contact us. One of the best places to
| Name | | |
| -------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | ![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square) |
| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |

View File

@@ -54,12 +54,6 @@ yarn dev
See [building desktop client app](../building-desktop-client-app.md).
### `@affine/storybook`
```shell
yarn workspace @affine/storybook storybook
```
## What's next?
- [Behind the code](./behind-the-code.md)

View File

@@ -22,9 +22,7 @@
"build": "yarn nx build @affine/web",
"build:electron": "yarn nx build @affine/electron",
"build:storage": "yarn nx run-many -t build -p @affine/storage",
"build:storybook": "yarn nx build @affine/storybook",
"start:web-static": "yarn workspace @affine/web static-server",
"start:storybook": "yarn exec serve tests/storybook/storybook-static -l 6006",
"serve:test-static": "yarn exec serve tests/fixtures --cors -p 8081",
"lint:eslint": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" eslint . --ext .js,mjs,.ts,.tsx --cache",
"lint:eslint:fix": "yarn lint:eslint --fix",

View File

@@ -1,71 +0,0 @@
import { runCli } from '@magic-works/i18n-codegen';
import type { StorybookConfig } from '@storybook/react-vite';
import { fileURLToPath } from 'node:url';
import { mergeConfig, type InlineConfig } from 'vite';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { getRuntimeConfig } from '@affine/cli/src/webpack/runtime-config';
runCli(
{
config: fileURLToPath(
new URL('../../../.i18n-codegen.json', import.meta.url)
),
watch: false,
},
error => {
console.error(error);
process.exit(1);
}
);
export default {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
staticDirs: ['../../../packages/frontend/core/public'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'storybook-dark-mode',
'storybook-addon-react-router-v6',
],
framework: {
name: '@storybook/react-vite',
},
async viteFinal(config, _options) {
const runtimeConfig = getRuntimeConfig({
distribution: 'browser',
mode: 'development',
channel: 'canary',
coverage: false,
});
// disable for storybook build
runtimeConfig.enableCloud = false;
return mergeConfig<InlineConfig, InlineConfig>(config, {
assetsInclude: ['**/*.md', '**/*.zip'],
resolve: {
alias: {
// workaround for https://github.com/vitejs/vite/issues/9731
// it seems vite does not resolve self reference correctly?
'@affine/core': fileURLToPath(
new URL('../../../packages/frontend/core/src', import.meta.url)
),
},
},
esbuild: {
target: 'ES2022',
},
plugins: [vanillaExtractPlugin()],
define: {
'process.on': 'undefined',
'process.env': {},
'process.env.COVERAGE': JSON.stringify(!!process.env.COVERAGE),
'process.env.SHOULD_REPORT_TRACE': `${Boolean(
process.env.SHOULD_REPORT_TRACE === 'true'
)}`,
'process.env.TRACE_REPORT_ENDPOINT': `"${process.env.TRACE_REPORT_ENDPOINT}"`,
'process.env.CAPTCHA_SITE_KEY': `"${process.env.CAPTCHA_SITE_KEY}"`,
runtimeConfig: runtimeConfig,
},
});
},
} as StorybookConfig;

View File

@@ -1,2 +0,0 @@
import 'core-js/modules/esnext.symbol.async-dispose';
import 'core-js/modules/esnext.symbol.dispose';

View File

@@ -1,3 +0,0 @@
<script>
window.global = window;
</script>

View File

@@ -1,193 +0,0 @@
import './polyfill';
import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css';
import '@affine/core/bootstrap/preload';
import { createI18n } from '@affine/i18n';
import { ThemeProvider, useTheme } from 'next-themes';
import { useDarkMode } from 'storybook-dark-mode';
import { AffineContext } from '@affine/component/context';
import useSWR from 'swr';
import type { Decorator } from '@storybook/react';
import {
FrameworkRoot,
FrameworkScope,
GlobalContextService,
LifecycleService,
WorkspacesService,
_setCurrentStore,
configureTestingInfraModules,
useLiveData,
} from '@toeverything/infra';
import { setupGlobal, type Environment } from '@affine/env/global';
import type { Preview } from '@storybook/react';
import { useLayoutEffect, useMemo, useRef } from 'react';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { Framework } from '@toeverything/infra';
import { configureCommonModules } from '@affine/core/modules';
import { createStore } from 'jotai';
setupGlobal();
export const parameters = {
backgrounds: { disable: true },
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
const i18n = createI18n();
const withI18n: Decorator = (Story, context) => {
const locale = context.globals.locale;
useSWR(
locale,
async () => {
await i18n.changeLanguage(locale);
},
{
suspense: true,
}
);
return <Story {...context} />;
};
const ThemeChange = () => {
const isDark = useDarkMode();
const theme = useTheme();
if (theme.resolvedTheme === 'dark' && !isDark) {
theme.setTheme('light');
} else if (theme.resolvedTheme === 'light' && isDark) {
theme.setTheme('dark');
}
return null;
};
localStorage.clear();
// do not show onboarding for storybook
window.localStorage.setItem('app_config', '{"onBoarding":false}');
window.localStorage.setItem('dismissAiOnboarding', 'true');
window.localStorage.setItem('dismissAiOnboardingEdgeless', 'true');
window.localStorage.setItem('dismissAiOnboardingLocal', 'true');
const framework = new Framework();
configureCommonModules(framework);
configureTestingInfraModules(framework);
const frameworkProvider = framework.provider();
frameworkProvider.get(LifecycleService).applicationStart();
const globalContextService = frameworkProvider.get(GlobalContextService);
const store = createStore();
_setCurrentStore(store);
frameworkProvider
.get(WorkspacesService)
.create(WorkspaceFlavour.LOCAL, async w => {
w.meta.setName('test-workspace');
w.meta.writeVersion(w);
})
.then(meta => {
globalContextService.globalContext.workspaceId.set(meta.id);
});
const withContextDecorator: Decorator = (Story, context) => {
const workspaceId = useLiveData(
globalContextService.globalContext.workspaceId.$
);
const { workspace } =
useMemo(() => {
if (!workspaceId) {
return null;
}
return frameworkProvider.get(WorkspacesService).open({
metadata: { flavour: WorkspaceFlavour.LOCAL, id: workspaceId },
});
}, []) ?? {};
if (!workspace) {
return <>loading..</>;
}
return (
<FrameworkRoot framework={frameworkProvider}>
<FrameworkScope scope={workspace.scope}>
<ThemeProvider>
<AffineContext store={store}>
<ThemeChange />
<Story {...context} />
</AffineContext>
</ThemeProvider>
</FrameworkScope>
</FrameworkRoot>
);
};
const platforms = ['web', 'desktop-macos', 'desktop-windows'] as const;
const withPlatformSelectionDecorator: Decorator = (Story, context) => {
const setupCounterRef = useRef(0);
useLayoutEffect(() => {
if (setupCounterRef.current++ === 0) {
return;
}
switch (context.globals.platform) {
case 'desktop-macos':
environment = {
...environment,
isBrowser: true,
isDesktop: true,
isMacOs: true,
isWindows: false,
} as Environment;
break;
case 'desktop-windows':
environment = {
...environment,
isBrowser: true,
isDesktop: true,
isMacOs: false,
isWindows: true,
} as Environment;
break;
default:
globalThis.$AFFINE_SETUP = false;
setupGlobal();
break;
}
}, [context.globals.platform]);
return <Story key={context.globals.platform} {...context} />;
};
const decorators = [
withContextDecorator,
withI18n,
withPlatformSelectionDecorator,
];
const preview: Preview = {
decorators,
globalTypes: {
platform: {
description: 'Rendering platform target',
defaultValue: 'web',
toolbar: {
// The label to show for this toolbar item
title: 'platform',
// Array of plain string values or MenuItem shape (see below)
items: platforms,
// Change title based on selected value
dynamicTitle: true,
},
},
},
};
export default preview;

View File

@@ -1 +0,0 @@
# Storybook

View File

@@ -1,63 +0,0 @@
{
"name": "@affine/storybook",
"private": true,
"scripts": {
"dev": "storybook dev -p 6006",
"build": "storybook build",
"test": "test-storybook"
},
"dependencies": {
"@affine/cli": "workspace:*",
"@affine/component": "workspace:*",
"@affine/i18n": "workspace:*",
"@dnd-kit/sortable": "^8.0.0",
"@storybook/jest": "^0.2.3",
"@storybook/testing-library": "^0.2.2",
"foxact": "^0.2.33",
"jotai": "^2.8.0",
"lodash-es": "^4.17.21",
"nanoid": "^5.0.7",
"react-router-dom": "^6.22.3",
"ses": "^1.4.1",
"storybook-addon-react-router-v6": "^2.0.15"
},
"devDependencies": {
"@blocksuite/block-std": "0.14.0-canary-202404151235-655ec84",
"@blocksuite/blocks": "0.14.0-canary-202404151235-655ec84",
"@blocksuite/global": "0.14.0-canary-202404151235-655ec84",
"@blocksuite/icons": "2.1.46",
"@blocksuite/inline": "0.14.0-canary-202404151235-655ec84",
"@blocksuite/presets": "0.14.0-canary-202404151235-655ec84",
"@blocksuite/store": "0.14.0-canary-202404151235-655ec84",
"@storybook/addon-actions": "^7.6.17",
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-interactions": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
"@storybook/addon-storysource": "^7.6.17",
"@storybook/blocks": "^7.6.17",
"@storybook/builder-vite": "^7.6.17",
"@storybook/react": "^7.6.17",
"@storybook/react-vite": "^7.6.17",
"@storybook/test-runner": "^0.17.0",
"@vanilla-extract/esbuild-plugin": "^2.3.5",
"@vitejs/plugin-react": "^4.2.1",
"chromatic": "^11.3.0",
"concurrently": "^8.2.2",
"jest-mock": "^29.7.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"serve": "^14.2.1",
"storybook": "^7.6.17",
"storybook-dark-mode": "^3.0.3",
"storybook-mock-date-decorator": "^1.0.2",
"wait-on": "^7.2.0"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
"@blocksuite/global": "*",
"@blocksuite/icons": "2.1.34",
"@blocksuite/presets": "*",
"@blocksuite/store": "*"
},
"version": "0.14.0"
}

View File

@@ -1,49 +0,0 @@
{
"name": "@affine/storybook",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"targets": {
"build": {
"executor": "nx:run-script",
"dependsOn": ["^build"],
"inputs": [
"default",
"^production",
"{projectRoot}/.storybook/**/*",
"{workspaceRoot}/packages/frontend/components/src/**/*",
"{workspaceRoot}/packages/frontend/core/src/**/*",
"{workspaceRoot}/packages/common/infra/**/*",
{
"runtime": "node -v"
},
{
"env": "BUILD_TYPE"
},
{
"env": "PERFSEE_TOKEN"
},
{
"env": "SENTRY_ORG"
},
{
"env": "SENTRY_PROJECT"
},
{
"env": "SENTRY_AUTH_TOKEN"
},
{
"env": "SENTRY_DSN"
},
{
"env": "DISTRIBUTION"
},
{
"env": "COVERAGE"
}
],
"options": {
"script": "build"
},
"outputs": ["{projectRoot}/storybook-static"]
}
}
}

View File

@@ -1,40 +0,0 @@
import { BrowserWarning, LocalDemoTips } from '@affine/component/affine-banner';
import type { StoryFn } from '@storybook/react';
import { useState } from 'react';
export default {
title: 'AFFiNE/Banner',
component: BrowserWarning,
};
export const Default: StoryFn = () => {
const [closed, setIsClosed] = useState(true);
return (
<div>
<BrowserWarning
message={<span>test</span>}
show={closed}
onClose={() => {
setIsClosed(false);
}}
/>
</div>
);
};
export const Download: StoryFn = () => {
const [, setIsClosed] = useState(true);
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
<LocalDemoTips
isLoggedIn={isLoggedIn}
onLogin={() => setIsLoggedIn(true)}
onEnableCloud={() => {}}
onClose={() => {
setIsClosed(false);
}}
/>
</div>
);
};

View File

@@ -1,146 +0,0 @@
import {
AddPageButton,
AppSidebar,
AppSidebarFallback,
appSidebarOpenAtom,
CategoryDivider,
MenuLinkItem,
navHeaderStyle,
QuickSearchInput,
SidebarContainer,
SidebarScrollableContainer,
SidebarSwitch,
} from '@affine/core/components/app-sidebar';
import { DeleteTemporarilyIcon, SettingsIcon } from '@blocksuite/icons';
import type { Meta, StoryFn } from '@storybook/react';
import { useAtom } from 'jotai';
import type { PropsWithChildren } from 'react';
import { useState } from 'react';
import { MemoryRouter } from 'react-router-dom';
export default {
title: 'AFFiNE/AppSidebar',
component: AppSidebar,
} satisfies Meta;
const Container = ({ children }: PropsWithChildren) => (
<MemoryRouter>
<main
style={{
position: 'relative',
width: '100vw',
height: 'calc(100vh - 40px)',
overflow: 'hidden',
display: 'flex',
flexDirection: 'row',
}}
>
{children}
</main>
</MemoryRouter>
);
const Main = () => {
const [open] = useAtom(appSidebarOpenAtom);
return (
<div>
<div className={navHeaderStyle}>
<SidebarSwitch show={!open} />
</div>
</div>
);
};
export const Default: StoryFn = () => {
return (
<Container>
<AppSidebar />
<Main />
</Container>
);
};
export const Fallback = () => {
return (
<Container>
<AppSidebarFallback />
<Main />
</Container>
);
};
export const WithItems: StoryFn = () => {
const [collapsed, setCollapsed] = useState(false);
return (
<Container>
<AppSidebar>
<SidebarContainer>
<QuickSearchInput />
<div style={{ height: '20px' }} />
<MenuLinkItem
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Settings
</MenuLinkItem>
<MenuLinkItem
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Settings
</MenuLinkItem>
<MenuLinkItem
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Settings
</MenuLinkItem>
</SidebarContainer>
<SidebarScrollableContainer>
<CategoryDivider label="Favorites" />
<MenuLinkItem
collapsed={collapsed}
onCollapsedChange={setCollapsed}
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Collapsible Item
</MenuLinkItem>
<MenuLinkItem
collapsed={!collapsed}
onCollapsedChange={setCollapsed}
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Collapsible Item
</MenuLinkItem>
<MenuLinkItem
icon={<SettingsIcon />}
to="/test"
onClick={() => alert('opened')}
>
Settings
</MenuLinkItem>
<CategoryDivider label="Others" />
<MenuLinkItem
icon={<DeleteTemporarilyIcon />}
to="/test"
onClick={() => alert('opened')}
>
Trash
</MenuLinkItem>
</SidebarScrollableContainer>
<SidebarContainer>
<AddPageButton />
</SidebarContainer>
</AppSidebar>
<Main />
</Container>
);
};

View File

@@ -1,59 +0,0 @@
import type { AddPageButtonProps } from '@affine/core/components/app-sidebar';
import { AppUpdaterButton } from '@affine/core/components/app-sidebar';
import type { Meta, StoryFn } from '@storybook/react';
import type { PropsWithChildren } from 'react';
export default {
title: 'AFFiNE/AppUpdaterButton',
component: AppUpdaterButton,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof AppUpdaterButton>;
const Container = ({ children }: PropsWithChildren) => (
<main
style={{
position: 'relative',
width: '320px',
display: 'flex',
flexDirection: 'row',
backgroundColor: '#eee',
padding: '16px',
}}
>
{children}
</main>
);
export const Default: StoryFn<AddPageButtonProps> = props => {
return (
<Container>
<AppUpdaterButton {...props} />
</Container>
);
};
Default.args = {
appQuitting: false,
updateReady: true,
updateAvailable: {
version: '1.0.0-beta.1',
allowAutoUpdate: true,
},
downloadProgress: 42,
changelogUnread: true,
autoDownload: false,
};
export const Updated: StoryFn<AddPageButtonProps> = props => {
return (
<Container>
<AppUpdaterButton {...props} updateAvailable={null} />
</Container>
);
};
Updated.args = {
changelogUnread: true,
};

View File

@@ -1,57 +0,0 @@
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { AffineSchemas } from '@blocksuite/blocks/schemas';
import { DocCollection, Schema } from '@blocksuite/store';
import type { StoryFn } from '@storybook/react';
import { initEmptyPage } from '@toeverything/infra';
const schema = new Schema();
schema.register(AffineSchemas);
async function createAndInitPage(
docCollection: DocCollection,
title: string,
preview: string
) {
const doc = docCollection.createDoc();
initEmptyPage(doc, title);
doc.getBlockByFlavour('affine:paragraph').at(0)?.text?.insert(preview, 0);
return doc;
}
export default {
title: 'AFFiNE/BlocksuiteEditor/DocEditor',
};
export const DocEditor: StoryFn<typeof BlockSuiteEditor> = (_, { loaded }) => {
return (
<div style={{ height: '100vh' }}>
<BlockSuiteEditor mode="page" page={loaded.page} />
</div>
);
};
DocEditor.loaders = [
async () => {
const docCollection = new DocCollection({
id: 'test-workspace-id',
schema,
});
docCollection.doc.emit('sync', [true, docCollection.doc]);
docCollection.meta.setProperties({
tags: {
options: [],
},
});
const page = await createAndInitPage(
docCollection,
'This is page 1',
'Hello World from page 1'
);
return {
page,
workspace: docCollection,
};
},
];

View File

@@ -1,67 +0,0 @@
import { toast } from '@affine/component';
import { BlockCard } from '@affine/component/card/block-card';
import { WorkspaceCard } from '@affine/component/card/workspace-card';
import { Tooltip } from '@affine/component/ui/tooltip';
import { WorkspaceFlavour } from '@affine/env/workspace';
import {
EdgelessIcon,
ExportToHtmlIcon,
HelpIcon,
PageIcon,
} from '@blocksuite/icons';
import type { Meta } from '@storybook/react';
export default {
title: 'AFFiNE/Card',
component: WorkspaceCard,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const AffineWorkspaceCard = () => {
return (
<WorkspaceCard
meta={{
id: 'blocksuite-local',
flavour: WorkspaceFlavour.LOCAL,
}}
onClick={() => {}}
onSettingClick={() => {}}
currentWorkspaceId={null}
isOwner={true}
/>
);
};
export const AffineBlockCard = () => {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<BlockCard title={'New Page'} onClick={() => toast('clicked')} />
<BlockCard
title={'New Page'}
desc={'Write with a blank page'}
right={<PageIcon width={20} height={20} />}
onClick={() => toast('clicked page')}
/>
<BlockCard
title={'New Edgeless'}
desc={'Draw with a blank whiteboard'}
left={<PageIcon width={20} height={20} />}
right={<EdgelessIcon width={20} height={20} />}
onClick={() => toast('clicked edgeless')}
/>
<BlockCard
left={<ExportToHtmlIcon width={20} height={20} />}
title="HTML"
disabled
right={
<Tooltip content={'Learn how to Import pages into AFFiNE.'}>
<HelpIcon />
</Tooltip>
}
onClick={() => toast('click HTML')}
/>
</div>
);
};

View File

@@ -1,64 +0,0 @@
import { Checkbox } from '@affine/component';
import type { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
export default {
title: 'AFFiNE/Checkbox',
component: Checkbox,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof Checkbox>;
export const Basic: StoryFn<typeof Checkbox> = props => {
const [checked, setChecked] = useState(props.checked);
const handleChange = (
_event: React.ChangeEvent<HTMLInputElement>,
checked: boolean
) => {
setChecked(checked);
props.onChange?.(_event, checked);
};
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '1rem',
justifyContent: 'center',
}}
>
<Checkbox
style={{ fontSize: 14 }}
{...props}
checked={checked}
onChange={handleChange}
/>
<Checkbox
style={{ fontSize: 16 }}
{...props}
checked={checked}
onChange={handleChange}
/>
<Checkbox
style={{ fontSize: 18 }}
{...props}
checked={checked}
onChange={handleChange}
/>
<Checkbox
style={{ fontSize: 24 }}
{...props}
checked={checked}
onChange={handleChange}
/>
</div>
);
};
Basic.args = {
checked: true,
disabled: false,
indeterminate: false,
onChange: console.log,
};

View File

@@ -1,227 +0,0 @@
import { NavigateContext, topLevelRoutes } from '@affine/core/router';
import { assertExists } from '@blocksuite/global/utils';
import type { StoryFn } from '@storybook/react';
import { screen, userEvent, waitFor, within } from '@storybook/testing-library';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import {
reactRouterOutlets,
reactRouterParameters,
withRouter,
} from 'storybook-addon-react-router-v6';
import { mockDateDecorator } from 'storybook-mock-date-decorator';
const FakeApp = () => {
const location = useLocation();
const navigate = useNavigate();
// fixme: `key` is a hack to force the storybook to re-render the outlet
return (
<NavigateContext.Provider value={navigate}>
<Outlet key={location.pathname} />
</NavigateContext.Provider>
);
};
export default {
title: 'Preview/Core',
parameters: {
chromatic: { disableSnapshot: false },
},
};
export const Index: StoryFn = () => {
return <FakeApp />;
};
Index.decorators = [withRouter];
Index.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(
topLevelRoutes[0].children /* skip root wrapper */
),
}),
};
export const SettingPage: StoryFn = () => {
return <FakeApp />;
};
SettingPage.play = async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await waitFor(
async () => {
assertExists(
document.body.querySelector(
'[data-testid="slider-bar-workspace-setting-button"]'
)
);
},
{
timeout: 10000,
}
);
await step('click setting modal button', async () => {
await userEvent.click(
canvas.getByTestId('slider-bar-workspace-setting-button')
);
});
await waitFor(async () => {
assertExists(
document.body.querySelector('[data-testid="language-menu-button"]')
);
});
// Menu button may have "pointer-events: none" style, await 100ms to avoid this weird situation.
await new Promise(resolve => window.setTimeout(resolve, 100));
await step('click language menu button', async () => {
await userEvent.click(
document.body.querySelector(
'[data-testid="language-menu-button"]'
) as HTMLElement
);
});
};
SettingPage.decorators = [withRouter];
SettingPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
}),
};
export const NotFoundPage: StoryFn = () => {
return <FakeApp />;
};
NotFoundPage.decorators = [withRouter];
NotFoundPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
location: {
path: '/404',
},
}),
};
export const WorkspaceList: StoryFn = () => {
return <FakeApp />;
};
WorkspaceList.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
// click current-workspace
const currentWorkspace = await waitFor(
() => {
assertExists(canvas.getByTestId('current-workspace'));
return canvas.getByTestId('current-workspace');
},
{
timeout: 5000,
}
);
// todo: figure out why userEvent cannot click this element?
// await userEvent.click(currentWorkspace);
currentWorkspace.click();
};
WorkspaceList.decorators = [withRouter];
WorkspaceList.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
location: {
path: '/',
},
}),
};
export const SearchPage: StoryFn = () => {
return <FakeApp />;
};
SearchPage.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await waitFor(
async () => {
assertExists(
document.body.querySelector(
'[data-testid="slider-bar-quick-search-button"]'
)
);
},
{
timeout: 3000,
}
);
await userEvent.click(canvas.getByTestId('slider-bar-quick-search-button'));
await waitFor(
() => {
assertExists(screen.getByTestId('cmdk-quick-search'));
},
{
timeout: 3000,
}
);
};
SearchPage.decorators = [withRouter];
SearchPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
location: {
path: '/',
},
}),
};
export const ImportPage: StoryFn = () => {
return <FakeApp />;
};
ImportPage.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await waitFor(
async () => {
assertExists(
document.body.querySelector('[data-testid="sidebar-new-page-button"]')
);
},
{
timeout: 10000,
}
);
await userEvent.click(canvas.getByTestId('sidebar-new-page-button'));
await waitFor(() => {
assertExists(canvasElement.querySelector('v-line'));
});
await waitFor(() => {
assertExists(
canvasElement.querySelector('[data-testid="header-dropDownButton"]')
);
});
await userEvent.click(canvas.getByTestId('header-dropDownButton'));
await waitFor(() => {
assertExists(
document.body.querySelector('[data-testid="editor-option-menu-import"]')
);
});
await userEvent.click(screen.getByTestId('editor-option-menu-import'));
};
ImportPage.decorators = [withRouter, mockDateDecorator];
ImportPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
location: {
path: '/',
},
}),
date: new Date('Mon, 25 Mar 2024 08:39:07 GMT'),
};
export const OpenAppPage: StoryFn = () => {
return <FakeApp />;
};
OpenAppPage.decorators = [withRouter];
OpenAppPage.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
location: {
path: '/open-app/url',
searchParams: {
url: 'affine-beta://foo-bar.com',
open: 'false',
},
},
}),
};

View File

@@ -1,96 +0,0 @@
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { ImagePreviewModal } from '@affine/core/components/image-preview';
import type { Meta, StoryFn } from '@storybook/react';
import type { Doc } from '@toeverything/infra';
import {
DocsService,
FrameworkScope,
initEmptyPage,
useService,
WorkspaceService,
} from '@toeverything/infra';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { mockDateDecorator } from 'storybook-mock-date-decorator';
export default {
title: 'Component/ImagePreviewModal',
component: ImagePreviewModal,
} satisfies Meta;
export const Default: StoryFn = () => {
const workspace = useService(WorkspaceService).workspace;
const docsService = useService(DocsService);
const [doc, setDoc] = useState<Doc | null>(null);
useEffect(() => {
const bsDoc = workspace.docCollection.createDoc({ id: 'page0' });
initEmptyPage(bsDoc);
const { doc, release } = docsService.open(bsDoc.meta!.id);
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
.then(res => res.arrayBuffer())
.then(async buffer => {
const id = await workspace.docCollection.blob.set(
new Blob([buffer], { type: 'image/png' })
);
const frameId = bsDoc.getBlockByFlavour('affine:note')[0].id;
bsDoc.addBlock(
'affine:paragraph',
{
text: new bsDoc.Text(
'Please double click the image to preview it.'
),
},
frameId
);
bsDoc.addBlock(
'affine:image',
{
sourceId: id,
},
frameId
);
})
.catch(err => {
console.error('Failed to load large-image.png', err);
});
setDoc(doc);
return () => {
release();
};
}, [docsService, workspace]);
if (!doc) {
return <div />;
}
return (
<FrameworkScope scope={doc.scope}>
<div
style={{
height: '100vh',
width: '100vw',
overflow: 'auto',
}}
>
<BlockSuiteEditor mode="page" page={doc.blockSuiteDoc} />
{createPortal(
<ImagePreviewModal
pageId={doc.id}
docCollection={doc.blockSuiteDoc.collection}
/>,
document.body
)}
</div>
</FrameworkScope>
);
};
Default.decorators = [mockDateDecorator];
Default.parameters = {
date: new Date('Mon, 25 Mar 2024 08:39:07 GMT'),
};

View File

@@ -1,22 +0,0 @@
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
import { toast } from '@affine/component';
import { ImportPage } from '@affine/component/import-page';
import type { Meta, StoryFn } from '@storybook/react';
export default {
title: 'AFFiNE/ImportPage',
component: ImportPage,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
const Template: StoryFn<typeof ImportPage> = args => <ImportPage {...args} />;
export const Basic = Template.bind(undefined);
Basic.args = {
importHtml: () => toast('Click importHtml'),
importMarkdown: () => toast('Click importMarkdown'),
importNotion: () => toast('Click importNotion'),
onClose: () => toast('Click onClose'),
};

View File

@@ -1,42 +0,0 @@
import { Input } from '@affine/component';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
export default {
title: 'AFFiNE/Input',
component: Input,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof Input>;
export const Basic: StoryFn<typeof Input> = () => {
return <Input data-testid="test-input" defaultValue="test" />;
};
Basic.play = async ({ canvasElement }) => {
const element = within(canvasElement);
const item = element.getByTestId('test-input') as HTMLInputElement;
expect(item).toBeTruthy();
expect(item.value).toBe('test');
await userEvent.clear(item);
await userEvent.type(item, 'test 2');
expect(item.value).toBe('test 2');
};
export const DynamicHeight: StoryFn<typeof Input> = () => {
return <Input style={{ width: '200px' }} data-testid="test-input" />;
};
DynamicHeight.play = async ({ canvasElement }) => {
const element = within(canvasElement);
const item = element.getByTestId('test-input') as HTMLInputElement;
expect(item).toBeTruthy();
// FIXME: the following is not correct
// expect(item.getBoundingClientRect().width).toBe(200);
};
export const NoBorder: StoryFn<typeof Input> = () => {
return <Input noBorder={true} data-testid="test-input" />;
};

View File

@@ -1,18 +0,0 @@
import { Meta } from '@storybook/blocks';
<Meta title="Introduction" />
# AFFiNE UI Storybook
This is a UI component dev environment for AFFiNE UI.
It allows you to browse the AFFiNE UI components,
view the different states of each component,
and interactively develop and test components.
## Bug Reporting
If you find a bug, please file an issue on [GitHub](https://github.com/toeverything/AFFiNE/issues)
## Contributing
We welcome contributions from the community! [Get started here](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md)

View File

@@ -1,249 +0,0 @@
import {
expandNotificationCenterAtom,
NotificationCenter,
pushNotificationAtom,
} from '@affine/component/notification-center';
import type { Meta } from '@storybook/react';
import { useAtomValue, useSetAtom } from 'jotai';
export default {
title: 'AFFiNE/NotificationCenter',
component: NotificationCenter,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof NotificationCenter>;
let id = 0;
const image = (
<video autoPlay muted loop>
<source
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
type="video/mp4"
/>
</video>
);
export const Basic = () => {
const push = useSetAtom(pushNotificationAtom);
const expand = useAtomValue(expandNotificationCenterAtom);
return (
<>
<div>{expand ? 'expanded' : 'collapsed'}</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: `${key} message`,
timeout: 3000,
progressingBar: true,
action: async () => {
console.log('undo');
},
type: 'info',
});
}}
>
Push timeout notification
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: `${key} message`,
type: 'info',
});
}}
>
Push notification with no timeout
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'info',
});
}}
>
Push notification with no message
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'success',
theme: 'light',
});
}}
>
light success
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'success',
theme: 'dark',
});
}}
>
dark success
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'info',
theme: 'light',
});
}}
>
light info
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'info',
theme: 'dark',
});
}}
>
dark info
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'warning',
theme: 'light',
});
}}
>
light warning
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'warning',
theme: 'dark',
});
}}
>
dark warning
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'error',
theme: 'light',
});
}}
>
light error
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: ``,
type: 'error',
theme: 'dark',
});
}}
>
dark error
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: `gif test`,
type: 'info',
multimedia: image,
timeout: 3000,
action: async () => {
console.log('undo');
},
progressingBar: true,
});
}}
>
gif
</button>
</div>
<div>
<button
onClick={() => {
const key = id++;
push({
title: `${key} title`,
type: 'info',
theme: 'default',
timeout: 3000,
});
}}
>
default message
</button>
</div>
<NotificationCenter />
</>
);
};

View File

@@ -1,14 +0,0 @@
import type { Input } from '@affine/component';
import { Onboarding } from '@affine/core/components/affine/onboarding/onboarding';
import type { Meta, StoryFn } from '@storybook/react';
export default {
title: 'Preview/Onboarding',
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof Input>;
export const Preview: StoryFn<typeof Onboarding> = () => {
return <Onboarding />;
};

View File

@@ -1,14 +0,0 @@
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import type { Meta } from '@storybook/react';
export default {
title: 'AFFiNE/PageDetailSkeleton',
component: PageDetailSkeleton,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<typeof PageDetailSkeleton>;
export const Basic = () => {
return <PageDetailSkeleton />;
};

View File

@@ -1,64 +0,0 @@
import { PagePropertiesTable } from '@affine/core/components/affine/page-properties';
import { AffineSchemas } from '@blocksuite/blocks/schemas';
import { DocCollection, Schema } from '@blocksuite/store';
import type { StoryFn } from '@storybook/react';
import { initEmptyPage } from '@toeverything/infra';
const schema = new Schema();
schema.register(AffineSchemas);
async function createAndInitPage(
docCollection: DocCollection,
title: string,
preview: string
) {
const page = docCollection.createDoc();
initEmptyPage(page, title);
page.getBlockByFlavour('affine:paragraph').at(0)?.text?.insert(preview, 0);
return page;
}
export default {
title: 'AFFiNE/PageInfoProperties',
};
export const PageInfoProperties: StoryFn<typeof PagePropertiesTable> = (
_,
{ loaded }
) => {
return (
<div style={{ height: '100vh' }}>
<PagePropertiesTable page={loaded.page} />
</div>
);
};
PageInfoProperties.loaders = [
async () => {
const docCollection = new DocCollection({
id: 'test-workspace-id',
schema,
});
docCollection.doc.emit('sync', [true, docCollection.doc]);
docCollection.meta.setProperties({
tags: {
options: [],
},
});
const page = await createAndInitPage(
docCollection,
'This is page 1',
'Hello World from page 1'
);
if (page.meta) {
page.meta.updatedDate = Date.now();
}
return {
page,
workspace: docCollection,
};
},
];

View File

@@ -1,349 +0,0 @@
import { toast } from '@affine/component';
import type {
ListItem,
ListProps,
PageListItemProps,
PageOperationCellProps,
PageTagsProps,
} from '@affine/core/components/page-list';
import {
FloatingToolbar,
List,
ListScrollContainer,
NewPageButton,
PageListItem,
PageOperationCell,
PageTags,
} from '@affine/core/components/page-list';
import { topLevelRoutes } from '@affine/core/router';
import { AffineSchemas } from '@blocksuite/blocks/schemas';
import { PageIcon, TagsIcon } from '@blocksuite/icons';
import { DocCollection, Schema } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { userEvent } from '@storybook/testing-library';
import { initEmptyPage } from '@toeverything/infra';
import { useState } from 'react';
import {
reactRouterOutlets,
reactRouterParameters,
withRouter,
} from 'storybook-addon-react-router-v6';
export default {
title: 'AFFiNE/PageList',
parameters: {
layout: 'fullscreen',
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const AffineOperationCell: StoryFn<PageOperationCellProps> = ({
...props
}) => <PageOperationCell {...props} />;
AffineOperationCell.args = {
page: {
id: '123',
title: 'Test Page Title',
tags: ['tag1', 'tag2'],
createDate: new Date('2021-01-01').getTime(),
},
};
AffineOperationCell.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
}),
};
AffineOperationCell.play = async ({ canvasElement }) => {
{
const button = canvasElement.querySelector(
'[data-testid="page-list-operation-button"]'
) as HTMLButtonElement;
expect(button).not.toBeNull();
await userEvent.click(button);
}
};
export const AffineNewPageButton: StoryFn<typeof NewPageButton> = ({
...props
}) => <NewPageButton {...props} />;
AffineNewPageButton.args = {
createNewPage: () => toast('Create new page'),
createNewEdgeless: () => toast('Create new edgeless'),
};
AffineNewPageButton.play = async ({ canvasElement }) => {
const button = canvasElement.querySelector('button') as HTMLButtonElement;
expect(button).not.toBeNull();
const dropdown = button.querySelector('svg') as SVGSVGElement;
expect(dropdown).not.toBeNull();
await userEvent.click(dropdown);
};
const testTags = [
{
color: 'red',
id: 'test-tag-id-cccc',
value: 'cccccccccccccccc',
},
{
color: 'red',
id: 'test-tag-id-a',
value: 'a',
},
{
color: 'red',
id: 'test-tag-id-b',
value: 'b',
},
{
color: 'red',
id: 'test-tag-id-c',
value: 'c',
},
{
color: 'red',
id: 'test-tag-id-d',
value: 'd',
},
{
color: 'red',
id: 'test-tag-id-0',
value: 'foo',
},
{
color: 'pink',
id: 'test-tag-id-1',
value: 'bar',
},
{
color: 'purple',
id: 'test-tag-id-2',
value: 'foobar',
},
{
color: 'black',
id: 'test-tag-id-3',
value: 'affine',
},
{
color: 'orange',
id: 'test-tag-id-4',
value: 'blocksuite',
},
{
color: 'yellow',
id: 'test-tag-id-5',
value: 'toeverything',
},
{
color: 'green',
id: 'test-tag-id-6',
value: 'toeverything',
},
{
color: 'blue',
id: 'test-tag-id-7',
value: 'toeverything',
},
{
color: 'indigo',
id: 'test-tag-id-8',
value: 'toeverything',
},
{
color: 'teal',
id: 'test-tag-id-9',
value: 'toeverything',
},
{
color: 'cyan',
id: 'test-tag-id-10',
value: 'toeverything',
},
{
color: 'gray',
id: 'test-tag-id-11',
value: 'toeverything',
},
{
color: 'red',
id: 'test-tag-id-12',
value: 'toeverything',
},
];
export const PageListItemComponent: StoryFn<PageListItemProps> = props => (
<PageListItem {...props}></PageListItem>
);
PageListItemComponent.args = {
pageId: 'test-page-id',
title: 'Test Page Title',
preview:
'this is page preview and it is very long and will be truncated because it is too long and it is very long and will be truncated because it is too long',
icon: <PageIcon />,
to: '/hello',
selectable: true,
createDate: new Date('2021-01-01'),
updatedDate: new Date('2023-08-15'),
draggable: true,
tags: testTags,
selected: true,
};
PageListItemComponent.decorators = [withRouter];
export const ListItemTags: StoryFn<PageTagsProps> = props => (
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<div style={{ width: '300px' }}>
<PageTags {...props}></PageTags>
</div>
</div>
);
ListItemTags.args = {
// FIXME: this is a hack to make the storybook work
// tags: testTags,
hoverExpandDirection: 'left',
widthOnHover: 600,
maxItems: 5,
};
export const PageListStory: StoryFn<ListProps<ListItem>> = (
props,
{ loaded }
) => {
return (
<ListScrollContainer
style={{
height: '100vh',
}}
>
<List {...props} {...loaded}></List>
</ListScrollContainer>
);
};
PageListStory.args = {
groupBy: [
{
id: 'all',
label: count => `All Pages (${count})`,
match: () => true,
},
],
};
PageListStory.argTypes = {
selectable: {
control: 'radio',
options: [true, 'toggle', false],
},
hideHeader: {
type: 'boolean',
},
};
async function createAndInitPage(
docCollection: DocCollection,
title: string,
preview: string
) {
const doc = docCollection.createDoc();
initEmptyPage(doc, title);
doc.getBlockByFlavour('affine:paragraph').at(0)?.text?.insert(preview, 0);
return doc;
}
PageListStory.loaders = [
async () => {
const schema = new Schema();
schema.register(AffineSchemas);
const docCollection = new DocCollection({
id: 'test-workspace-id',
schema,
});
docCollection.meta.setProperties({
tags: {
options: structuredClone(testTags),
},
});
const page1 = await createAndInitPage(
docCollection,
'This is page 1',
'Hello World from page 1'
);
const page2 = await createAndInitPage(
docCollection,
'This is page 2',
'Hello World from page 2'
);
const page3 = await createAndInitPage(
docCollection,
'This is page 3',
'Hello World from page 3Hello World from page 3Hello World from page 3Hello World from page 3Hello World from page 3'
);
await createAndInitPage(
docCollection,
'This is page 4',
'Hello World from page 3Hello World from page 3Hello World from page 3Hello World from page 3Hello World from page 3'
);
page1.meta!.createDate = new Date('2021-01-01').getTime();
page2.meta!.createDate = page2.meta!.createDate - 3600 * 1000 * 24;
page3.meta!.createDate = page3.meta!.createDate - 3600 * 1000 * 24 * 7;
docCollection.meta.docMetas[3].tags = testTags.slice(0, 3).map(t => t.id);
docCollection.meta.docMetas[2].tags = testTags.slice(0, 12).map(t => t.id);
return {
blockSuiteWorkspace: docCollection,
pages: docCollection.meta.docs,
};
},
];
export const FloatingToolbarStory: StoryFn<typeof FloatingToolbar> = props => {
const [open, setOpen] = useState(false);
return (
<div
style={{
height: '100vh',
overflow: 'auto',
}}
>
<button
style={{
border: '1px solid black',
padding: '10px',
}}
onClick={() => setOpen(o => !o)}
>
{open ? 'hide' : 'show'}
</button>
<FloatingToolbar
style={{ position: 'fixed', bottom: '20px', width: '100%' }}
{...props}
open={open}
>
<FloatingToolbar.Item>10 Selected</FloatingToolbar.Item>
<FloatingToolbar.Separator />
<FloatingToolbar.Button
icon={<TagsIcon />}
label="Add Tags"
onClick={console.log}
/>
<FloatingToolbar.Button
icon={<TagsIcon />}
label="Add Tags"
onClick={console.log}
/>
</FloatingToolbar>
</div>
);
};

View File

@@ -1,104 +0,0 @@
import {
registerAffineCreationCommands,
registerAffineLayoutCommands,
registerAffineSettingsCommands,
} from '@affine/core/commands';
import { CMDKQuickSearchModal } from '@affine/core/components/pure/cmdk';
import { HighlightLabel } from '@affine/core/components/pure/cmdk/highlight';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Doc } from '@blocksuite/store';
import type { Meta, StoryFn } from '@storybook/react';
import { useStore } from 'jotai';
import { useEffect, useState } from 'react';
import { withRouter } from 'storybook-addon-react-router-v6';
export default {
title: 'AFFiNE/QuickSearch',
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
const createMockedPage = () => {
return {
id: 'test-page',
waitForLoaded: () => Promise.resolve(),
} as any as Doc;
};
function useRegisterCommands() {
const t = useAFFiNEI18N();
const store = useStore();
useEffect(() => {
const unsubs = [
registerAffineSettingsCommands({
t,
store,
theme: {
setTheme: () => {},
theme: 'auto',
themes: ['auto', 'dark', 'light'],
},
languageHelper: {
onLanguageChange: () => {},
languagesList: [
{
tag: 'en',
name: 'English',
originalName: 'English',
Completeness: 1,
},
{
tag: 'zh-Hans',
name: 'Simplified Chinese',
originalName: '简体中文',
Completeness: 1,
},
],
currentLanguage: undefined,
},
editor: null,
}),
registerAffineCreationCommands({
t,
store,
pageHelper: {
createEdgeless: createMockedPage,
createPage: createMockedPage,
importFile: () => Promise.resolve(),
isPreferredEdgeless: () => false,
createLinkedPage: createMockedPage,
},
}),
registerAffineLayoutCommands({
t,
store,
}),
];
return () => {
unsubs.forEach(unsub => unsub());
};
}, [store, t]);
}
export const CMDKStoryWithCommands: StoryFn = () => {
useRegisterCommands();
return <CMDKQuickSearchModal open />;
};
CMDKStoryWithCommands.decorators = [withRouter];
export const HighlightStory: StoryFn = () => {
const [query, setQuery] = useState('');
const label = {
title: 'title',
};
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<HighlightLabel label={label} highlight={query} />
</>
);
};

View File

@@ -1,42 +0,0 @@
import { Button } from '@affine/component/ui/button';
import { CMDKContainer, CMDKModal } from '@affine/core/components/pure/cmdk';
import { useCMDKCommandGroups } from '@affine/core/components/pure/cmdk/data-hooks';
import type { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
export default {
title: 'AFFiNE/QuickSearch',
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const CMDKModalStory: StoryFn = () => {
const [open, setOpen] = useState(false);
const [counter, setCounter] = useState(0);
return (
<>
<Button onClick={() => setOpen(true)}>Open Modal</Button>
<CMDKModal key={counter} open={open} onOpenChange={setOpen}>
<Button onClick={() => setCounter(c => c + 1)}>
Trigger new modal
</Button>
</CMDKModal>
</>
);
};
export const CMDKPanelStory: StoryFn = () => {
const [query, setQuery] = useState('');
const groups = useCMDKCommandGroups();
return (
<CMDKModal open>
<CMDKContainer
open
query={query}
onQueryChange={setQuery}
groups={groups}
/>
</CMDKModal>
);
};

View File

@@ -1,113 +0,0 @@
import { toast } from '@affine/component';
import { PublicLinkDisableModal } from '@affine/component/disable-public-link';
import { ShareMenu } from '@affine/core/components/affine/share-page-modal/share-menu';
import { WorkspaceFlavour } from '@affine/env/workspace';
import type { Doc } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import {
initEmptyPage,
useService,
WorkspaceService,
} from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useEffect, useState } from 'react';
export default {
title: 'AFFiNE/ShareMenu',
component: ShareMenu,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
async function unimplemented() {
toast('work in progress');
}
export const Basic: StoryFn = () => {
const workspace = useService(WorkspaceService).workspace;
const [page, setPage] = useState<Doc | null>(null);
useEffect(() => {
const page = workspace.docCollection.createDoc({ id: nanoid() });
initEmptyPage(page);
setPage(page);
}, [workspace]);
if (!page) {
return <div></div>;
}
return (
<ShareMenu
currentPage={page}
workspaceMetadata={workspace}
onEnableAffineCloud={unimplemented}
/>
);
};
Basic.play = async ({ canvasElement }) => {
{
const button = canvasElement.querySelector(
'[data-testid="share-menu-button"]'
) as HTMLButtonElement;
expect(button).not.toBeNull();
button.click();
}
await new Promise(resolve => window.setTimeout(resolve, 100));
{
const button = canvasElement.querySelector(
'[data-testid="share-menu-enable-affine-cloud-button"]'
);
expect(button).not.toBeNull();
}
};
export const AffineBasic: StoryFn = () => {
const workspace = useService(WorkspaceService).workspace;
const [page, setPage] = useState<Doc | null>(null);
useEffect(() => {
const page = workspace.docCollection.createDoc({ id: nanoid() });
initEmptyPage(page);
setPage(page);
}, [workspace]);
if (!page) {
return <div></div>;
}
return (
<ShareMenu
currentPage={page}
workspaceMetadata={{
...workspace.meta,
flavour: WorkspaceFlavour.AFFINE_CLOUD,
}}
onEnableAffineCloud={unimplemented}
/>
);
};
export const DisableModal: StoryFn = () => {
const [open, setOpen] = useState(false);
return (
<>
<div onClick={() => setOpen(!open)}>Disable Public Link</div>
<PublicLinkDisableModal
open={open}
onConfirm={() => {
toast('Disabled');
setOpen(false);
}}
onOpenChange={setOpen}
/>
</>
);
};

View File

@@ -1,15 +0,0 @@
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
import { Switch } from '@affine/component';
import type { Meta, StoryFn } from '@storybook/react';
export default {
title: 'AFFiNE/Switch',
component: Switch,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta;
export const Basic: StoryFn = () => {
return <Switch>Switch</Switch>;
};

View File

@@ -1,32 +0,0 @@
import type { WorkspaceListProps } from '@affine/component/workspace-list';
import { WorkspaceList } from '@affine/component/workspace-list';
import type { Meta } from '@storybook/react';
import {
useLiveData,
useService,
WorkspacesService,
} from '@toeverything/infra';
export default {
title: 'AFFiNE/WorkspaceList',
component: WorkspaceList,
parameters: {
chromatic: { disableSnapshot: true },
},
} satisfies Meta<WorkspaceListProps>;
export const Default = () => {
const list = useLiveData(useService(WorkspacesService).list.workspaces$);
return (
<WorkspaceList
currentWorkspaceId={null}
items={list}
onClick={() => {}}
onSettingClick={() => {}}
onDragEnd={_ => {}}
useWorkspaceAvatar={() => undefined}
useWorkspaceName={() => undefined}
useIsWorkspaceOwner={() => false}
/>
);
};

View File

@@ -1,31 +0,0 @@
{
"extends": "../../tsconfig.json",
"include": ["./src/**/*", "./.storybook/**/*"],
"compilerOptions": {
// Workaround for storybook build
"baseUrl": "../..",
"composite": true,
"noEmit": false,
"outDir": "lib"
},
"references": [
{
"path": "../../packages/frontend/core"
},
{
"path": "../../packages/frontend/component"
},
{
"path": "../../tools/cli"
},
{
"path": "../../packages/common/env"
},
{
"path": "../../packages/common/infra"
},
{
"path": "./tsconfig.node.json"
}
]
}

View File

@@ -1,25 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"module": "ESNext",
"jsx": "react-jsx",
"moduleResolution": "bundler",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"noEmit": false,
"outDir": "lib/.storybook"
},
"include": [".storybook"],
"exclude": ["lib"],
"references": [
{ "path": "../../packages/frontend/core" },
{ "path": "../../packages/frontend/i18n" },
{
"path": "../../packages/common/env"
},
{
"path": "../../packages/frontend/core/tsconfig.node.json"
}
]
}

View File

@@ -126,9 +126,6 @@
{
"path": "./tests/kit"
},
{
"path": "./tests/storybook"
},
{
"path": "./tests/affine-local"
},

213
yarn.lock
View File

@@ -764,61 +764,6 @@ __metadata:
languageName: unknown
linkType: soft
"@affine/storybook@workspace:tests/storybook":
version: 0.0.0-use.local
resolution: "@affine/storybook@workspace:tests/storybook"
dependencies:
"@affine/cli": "workspace:*"
"@affine/component": "workspace:*"
"@affine/i18n": "workspace:*"
"@blocksuite/block-std": "npm:0.14.0-canary-202404151235-655ec84"
"@blocksuite/blocks": "npm:0.14.0-canary-202404151235-655ec84"
"@blocksuite/global": "npm:0.14.0-canary-202404151235-655ec84"
"@blocksuite/icons": "npm:2.1.46"
"@blocksuite/inline": "npm:0.14.0-canary-202404151235-655ec84"
"@blocksuite/presets": "npm:0.14.0-canary-202404151235-655ec84"
"@blocksuite/store": "npm:0.14.0-canary-202404151235-655ec84"
"@dnd-kit/sortable": "npm:^8.0.0"
"@storybook/addon-actions": "npm:^7.6.17"
"@storybook/addon-essentials": "npm:^7.6.17"
"@storybook/addon-interactions": "npm:^7.6.17"
"@storybook/addon-links": "npm:^7.6.17"
"@storybook/addon-storysource": "npm:^7.6.17"
"@storybook/blocks": "npm:^7.6.17"
"@storybook/builder-vite": "npm:^7.6.17"
"@storybook/jest": "npm:^0.2.3"
"@storybook/react": "npm:^7.6.17"
"@storybook/react-vite": "npm:^7.6.17"
"@storybook/test-runner": "npm:^0.17.0"
"@storybook/testing-library": "npm:^0.2.2"
"@vanilla-extract/esbuild-plugin": "npm:^2.3.5"
"@vitejs/plugin-react": "npm:^4.2.1"
chromatic: "npm:^11.3.0"
concurrently: "npm:^8.2.2"
foxact: "npm:^0.2.33"
jest-mock: "npm:^29.7.0"
jotai: "npm:^2.8.0"
lodash-es: "npm:^4.17.21"
nanoid: "npm:^5.0.7"
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
react-router-dom: "npm:^6.22.3"
serve: "npm:^14.2.1"
ses: "npm:^1.4.1"
storybook: "npm:^7.6.17"
storybook-addon-react-router-v6: "npm:^2.0.15"
storybook-dark-mode: "npm:^3.0.3"
storybook-mock-date-decorator: "npm:^1.0.2"
wait-on: "npm:^7.2.0"
peerDependencies:
"@blocksuite/blocks": "*"
"@blocksuite/global": "*"
"@blocksuite/icons": 2.1.34
"@blocksuite/presets": "*"
"@blocksuite/store": "*"
languageName: unknown
linkType: soft
"@affine/templates@workspace:*, @affine/templates@workspace:packages/frontend/templates":
version: 0.0.0-use.local
resolution: "@affine/templates@workspace:packages/frontend/templates"
@@ -2245,7 +2190,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.14.0, @babel/core@npm:^7.18.5, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.12, @babel/core@npm:^7.22.5, @babel/core@npm:^7.22.9, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.5, @babel/core@npm:^7.23.9, @babel/core@npm:^7.7.5":
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.14.0, @babel/core@npm:^7.18.5, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.12, @babel/core@npm:^7.22.5, @babel/core@npm:^7.22.9, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.7.5":
version: 7.24.3
resolution: "@babel/core@npm:7.24.3"
dependencies:
@@ -3368,7 +3313,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-react-jsx-self@npm:^7.18.6, @babel/plugin-transform-react-jsx-self@npm:^7.23.3":
"@babel/plugin-transform-react-jsx-self@npm:^7.18.6":
version: 7.23.3
resolution: "@babel/plugin-transform-react-jsx-self@npm:7.23.3"
dependencies:
@@ -3379,7 +3324,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-react-jsx-source@npm:^7.19.6, @babel/plugin-transform-react-jsx-source@npm:^7.23.3":
"@babel/plugin-transform-react-jsx-source@npm:^7.19.6":
version: 7.23.3
resolution: "@babel/plugin-transform-react-jsx-source@npm:7.23.3"
dependencies:
@@ -3714,7 +3659,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
version: 7.23.9
resolution: "@babel/runtime@npm:7.23.9"
dependencies:
@@ -14437,7 +14382,7 @@ __metadata:
languageName: node
linkType: hard
"@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.18.0, @types/babel__core@npm:^7.20.5":
"@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.18.0":
version: 7.20.5
resolution: "@types/babel__core@npm:7.20.5"
dependencies:
@@ -15747,20 +15692,6 @@ __metadata:
languageName: node
linkType: hard
"@vanilla-extract/esbuild-plugin@npm:^2.3.5":
version: 2.3.5
resolution: "@vanilla-extract/esbuild-plugin@npm:2.3.5"
dependencies:
"@vanilla-extract/integration": "npm:^7.0.0"
peerDependencies:
esbuild: ">=0.17.6"
peerDependenciesMeta:
esbuild:
optional: true
checksum: 10/5ef46dc86a3121a2f1bfa6758511cc16a1dda434d6faa0d4cae278bcd83c9d1f577abc476dfa75442ffcadd477af30d58ce1f3582ddf4ba2198eecff828cada6
languageName: node
linkType: hard
"@vanilla-extract/integration@npm:^7.0.0, @vanilla-extract/integration@npm:^7.1.2":
version: 7.1.2
resolution: "@vanilla-extract/integration@npm:7.1.2"
@@ -15862,21 +15793,6 @@ __metadata:
languageName: node
linkType: hard
"@vitejs/plugin-react@npm:^4.2.1":
version: 4.2.1
resolution: "@vitejs/plugin-react@npm:4.2.1"
dependencies:
"@babel/core": "npm:^7.23.5"
"@babel/plugin-transform-react-jsx-self": "npm:^7.23.3"
"@babel/plugin-transform-react-jsx-source": "npm:^7.23.3"
"@types/babel__core": "npm:^7.20.5"
react-refresh: "npm:^0.14.0"
peerDependencies:
vite: ^4.2.0 || ^5.0.0
checksum: 10/d7fa6dacd3c246bcee482ff4b7037b2978b6ca002b79780ad4921e91ae4bc85ab234cfb94f8d4d825fed8488a0acdda2ff02b47c27b3055187c0727b18fc725e
languageName: node
linkType: hard
"@vitest/coverage-istanbul@npm:1.4.0":
version: 1.4.0
resolution: "@vitest/coverage-istanbul@npm:1.4.0"
@@ -18446,25 +18362,6 @@ __metadata:
languageName: node
linkType: hard
"chromatic@npm:^11.3.0":
version: 11.3.0
resolution: "chromatic@npm:11.3.0"
peerDependencies:
"@chromatic-com/cypress": ^0.*.* || ^1.0.0
"@chromatic-com/playwright": ^0.*.* || ^1.0.0
peerDependenciesMeta:
"@chromatic-com/cypress":
optional: true
"@chromatic-com/playwright":
optional: true
bin:
chroma: dist/bin.js
chromatic: dist/bin.js
chromatic-cli: dist/bin.js
checksum: 10/fd8d0678c93bebe45cf087496a8b4c44967d06003bc26f18d7b5ce25db00962efd7fbb700f098f3196f1d65b037050ca785da0e07bfc1766ed81b2f9bbbad23f
languageName: node
linkType: hard
"chrome-trace-event@npm:^1.0.2, chrome-trace-event@npm:^1.0.3":
version: 1.0.3
resolution: "chrome-trace-event@npm:1.0.3"
@@ -19023,13 +18920,6 @@ __metadata:
languageName: node
linkType: hard
"compare-versions@npm:^6.0.0":
version: 6.1.0
resolution: "compare-versions@npm:6.1.0"
checksum: 10/20f349e7f8ad784704c68265f4e660e2abbe2c3d5c75793184fccb85f0c5c0263260e01fdd4488376f6b74b0f069e16c9684463f7316b075716fb1581eb36b77
languageName: node
linkType: hard
"component-emitter@npm:^1.3.0":
version: 1.3.1
resolution: "component-emitter@npm:1.3.1"
@@ -19103,26 +18993,6 @@ __metadata:
languageName: node
linkType: hard
"concurrently@npm:^8.2.2":
version: 8.2.2
resolution: "concurrently@npm:8.2.2"
dependencies:
chalk: "npm:^4.1.2"
date-fns: "npm:^2.30.0"
lodash: "npm:^4.17.21"
rxjs: "npm:^7.8.1"
shell-quote: "npm:^1.8.1"
spawn-command: "npm:0.0.2"
supports-color: "npm:^8.1.1"
tree-kill: "npm:^1.2.2"
yargs: "npm:^17.7.2"
bin:
conc: dist/bin/concurrently.js
concurrently: dist/bin/concurrently.js
checksum: 10/dcb1aa69d9c611a7bda9d4fc0fe1e388f971d1744acec7e0d52dffa2ef55743f1266ec9292f414c5789b9f61734b3fce772bd005d4de9564a949fb121b97bae1
languageName: node
linkType: hard
"config-file-ts@npm:^0.2.4":
version: 0.2.6
resolution: "config-file-ts@npm:0.2.6"
@@ -19842,15 +19712,6 @@ __metadata:
languageName: node
linkType: hard
"date-fns@npm:^2.30.0":
version: 2.30.0
resolution: "date-fns@npm:2.30.0"
dependencies:
"@babel/runtime": "npm:^7.21.0"
checksum: 10/70b3e8ea7aaaaeaa2cd80bd889622a4bcb5d8028b4de9162cbcda359db06e16ff6e9309e54eead5341e71031818497f19aaf9839c87d1aba1e27bb4796e758a9
languageName: node
linkType: hard
"date-fns@npm:^3.6.0":
version: 3.6.0
resolution: "date-fns@npm:3.6.0"
@@ -29012,13 +28873,6 @@ __metadata:
languageName: node
linkType: hard
"mockdate@npm:^3.0.5":
version: 3.0.5
resolution: "mockdate@npm:3.0.5"
checksum: 10/ff74f43f56a12e1339e838aee623e37b6e4c378173905697f2a006a5c581eea9c71da54fcea76c27e7e744422e49395c668932bf09e67f024841240ffef91fd2
languageName: node
linkType: hard
"modern-ahocorasick@npm:^1.0.0":
version: 1.0.1
resolution: "modern-ahocorasick@npm:1.0.1"
@@ -31864,15 +31718,6 @@ __metadata:
languageName: node
linkType: hard
"react-inspector@npm:6.0.2":
version: 6.0.2
resolution: "react-inspector@npm:6.0.2"
peerDependencies:
react: ^16.8.4 || ^17.0.0 || ^18.0.0
checksum: 10/5d23ad0f6f920458abd4c01af1b3cbdbe8846c254762fd6cfff4df119c54e08dd98ce8e91acacafb8173c19f07de2066df5b8e6cb19425751c1929a2620cbe77
languageName: node
linkType: hard
"react-is@npm:18.1.0":
version: 18.1.0
resolution: "react-is@npm:18.1.0"
@@ -33873,13 +33718,6 @@ __metadata:
languageName: node
linkType: hard
"spawn-command@npm:0.0.2":
version: 0.0.2
resolution: "spawn-command@npm:0.0.2"
checksum: 10/f13e8c3c63abd4a0b52fb567eba5f7940d480c5ed3ec61781d38a1850f179b1196c39e6efa2bbd301f82c1bf1cd7807abc8fbd8fc8e44bcaa3975a124c0d1657
languageName: node
linkType: hard
"spawn-wrap@npm:^2.0.0":
version: 2.0.0
resolution: "spawn-wrap@npm:2.0.0"
@@ -34134,32 +33972,6 @@ __metadata:
languageName: node
linkType: hard
"storybook-addon-react-router-v6@npm:^2.0.15":
version: 2.0.15
resolution: "storybook-addon-react-router-v6@npm:2.0.15"
dependencies:
compare-versions: "npm:^6.0.0"
react-inspector: "npm: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-dom: ^6.4.0
peerDependenciesMeta:
react:
optional: true
react-dom:
optional: true
checksum: 10/a6fbebd0f9b44b2866920c61d40e591adf6dc1fcd058814f349a78cded41c239e8f08448edec747b660476d55c44623caadef2b2de90321279debe60bafba4b9
languageName: node
linkType: hard
"storybook-dark-mode@npm:^3.0.3":
version: 3.0.3
resolution: "storybook-dark-mode@npm:3.0.3"
@@ -34184,17 +33996,6 @@ __metadata:
languageName: node
linkType: hard
"storybook-mock-date-decorator@npm:^1.0.2":
version: 1.0.2
resolution: "storybook-mock-date-decorator@npm:1.0.2"
dependencies:
mockdate: "npm:^3.0.5"
peerDependencies:
"@storybook/addons": ">=6"
checksum: 10/c8e8b6966b58bd830c4837e6940328b9d32776c216bfcbc9f55bb38c5c192ab511670ff77505577737589bf83a7ad98538da1e62a2aa9c628e42e5998f2f6147
languageName: node
linkType: hard
"storybook@npm:^7.6.17":
version: 7.6.17
resolution: "storybook@npm:7.6.17"
@@ -34650,7 +34451,7 @@ __metadata:
languageName: node
linkType: hard
"supports-color@npm:^8.0.0, supports-color@npm:^8.1.1, supports-color@npm:~8.1.1":
"supports-color@npm:^8.0.0, supports-color@npm:~8.1.1":
version: 8.1.1
resolution: "supports-color@npm:8.1.1"
dependencies:
@@ -36735,7 +36536,7 @@ __metadata:
languageName: node
linkType: hard
"wait-on@npm:^7.0.0, wait-on@npm:^7.2.0":
"wait-on@npm:^7.0.0":
version: 7.2.0
resolution: "wait-on@npm:7.2.0"
dependencies: