diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d099d6edb9..d1f0e0dd92 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -147,10 +147,13 @@ jobs:
uses: ./.github/actions/setup-node
with:
electron-install: false
- - run: yarn chromatic
- working-directory: apps/storybook
- env:
- CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
+ - name: Publish to Chromatic
+ uses: chromaui/action@v1
+ with:
+ workingDir: apps/storybook
+ buildScriptName: build
+ onlyStoryNames: Preview/Core
+ projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
build-core:
name: Build @affine/core
diff --git a/apps/core/package.json b/apps/core/package.json
index dbdac75902..10d2a22728 100644
--- a/apps/core/package.json
+++ b/apps/core/package.json
@@ -8,6 +8,11 @@
"dev": "yarn -T run dev-core",
"static-server": "ts-node-esm ./server.mts"
},
+ "exports": {
+ "./app": "./src/app.tsx",
+ "./router": "./src/router.ts",
+ "./bootstrap/setup": "./src/bootstrap/setup.ts"
+ },
"dependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/component": "workspace:*",
diff --git a/apps/core/src/bootstrap/setup.ts b/apps/core/src/bootstrap/setup.ts
index e7b9f096e5..3005e7ba5f 100644
--- a/apps/core/src/bootstrap/setup.ts
+++ b/apps/core/src/bootstrap/setup.ts
@@ -169,7 +169,14 @@ function createFirstAppData() {
rootStore.set(rootWorkspacesMetadataAtom, result);
}
+let isSetup = false;
+
export async function setup() {
+ if (isSetup) {
+ console.warn('already setup');
+ return;
+ }
+ isSetup = true;
rootStore.set(
workspaceAdaptersAtom,
WorkspaceAdapters as Record<
diff --git a/apps/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx b/apps/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx
index 8244f8973f..7d738acce1 100644
--- a/apps/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx
+++ b/apps/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx
@@ -3,6 +3,8 @@ import type { HTMLAttributes } from 'react';
import type React 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';
type HoverAnimateControllerProps = {
@@ -52,7 +54,7 @@ export const PageSwitchItem = (
options={{
loop: false,
autoplay: false,
- animationData: require('./animation-data/page-hover.json'),
+ animationData: pageHover,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},
@@ -71,7 +73,7 @@ export const EdgelessSwitchItem = (
options={{
loop: false,
autoplay: false,
- animationData: require('./animation-data/edgeless-hover.json'),
+ animationData: edgelessHover,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},
diff --git a/apps/core/src/router.ts b/apps/core/src/router.ts
index 0d438ede0d..672bcbf5e2 100644
--- a/apps/core/src/router.ts
+++ b/apps/core/src/router.ts
@@ -1,41 +1,41 @@
+import type { RouteObject } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
-export const router = createBrowserRouter(
- [
- {
- 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'),
- },
- ],
+export const routes = [
{
- future: {
- v7_normalizeFormMethod: true,
- },
- }
-);
+ 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'),
+ },
+] satisfies [RouteObject, ...RouteObject[]];
+
+export const router = createBrowserRouter(routes, {
+ future: {
+ v7_normalizeFormMethod: true,
+ },
+});
diff --git a/apps/core/tsconfig.json b/apps/core/tsconfig.json
index eb0b796783..1809b66040 100644
--- a/apps/core/tsconfig.json
+++ b/apps/core/tsconfig.json
@@ -5,7 +5,7 @@
"typeRoots": ["../../node_modules", "../../node_modules/@types"],
"types": ["webpack-env", "ses", "affine__env"]
},
- "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json"],
"exclude": ["node_modules"],
"references": [
{
diff --git a/apps/storybook/.storybook/main.ts b/apps/storybook/.storybook/main.ts
index 817bb1865c..aa5d7ef2c6 100644
--- a/apps/storybook/.storybook/main.ts
+++ b/apps/storybook/.storybook/main.ts
@@ -28,6 +28,7 @@ export default {
'@storybook/addon-interactions',
'@storybook/addon-storysource',
'storybook-dark-mode',
+ 'storybook-addon-react-router-v6',
],
framework: {
name: '@storybook/react-vite',
diff --git a/apps/storybook/.storybook/preview.tsx b/apps/storybook/.storybook/preview.tsx
index 4c70a7ce5f..89525d8bee 100644
--- a/apps/storybook/.storybook/preview.tsx
+++ b/apps/storybook/.storybook/preview.tsx
@@ -1,13 +1,16 @@
import '@affine/component/theme/global.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 { setupGlobal } from '@affine/env/global';
import type { ComponentType } from 'react';
import { useEffect } from 'react';
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 = {
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 i18n = createI18n();
const withI18n = (Story: any, context: any) => {
@@ -59,10 +46,13 @@ const Component = () => {
export const decorators = [
(Story: ComponentType) => {
+ use(setupPromise);
return (
-
-
+
+
+
+
);
},
diff --git a/apps/storybook/package.json b/apps/storybook/package.json
index d3824e99b0..8c958d820d 100644
--- a/apps/storybook/package.json
+++ b/apps/storybook/package.json
@@ -4,8 +4,7 @@
"scripts": {
"dev": "storybook dev -p 6006",
"build": "storybook build",
- "test": "test-storybook",
- "chromatic": "npx chromatic --build-script-name build"
+ "test": "test-storybook"
},
"dependencies": {
"@affine/component": "workspace:*",
@@ -40,7 +39,8 @@
"@blocksuite/store": "0.0.0-20230814155455-ceb5d5d8-nightly",
"chromatic": "^6.22.0",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "storybook-addon-react-router-v6": "^2.0.4"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
diff --git a/apps/storybook/src/stories/core.stories.tsx b/apps/storybook/src/stories/core.stories.tsx
new file mode 100644
index 0000000000..e8023e3e8a
--- /dev/null
+++ b/apps/storybook/src/stories/core.stories.tsx
@@ -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 ;
+};
+
+const FakeApp = () => {
+ const location = useLocation();
+ // fixme: `key` is a hack to force the storybook to re-render the outlet
+ return ;
+};
+
+export default {
+ title: 'Preview/Core',
+};
+
+export const Index: StoryFn = () => {
+ return ;
+};
+
+Index.decorators = [withRouter, withCleanLocalStorage];
+Index.parameters = {
+ reactRouter: reactRouterParameters({
+ routing: reactRouterOutlets(routes),
+ }),
+};
+
+export const SettingPage: StoryFn = () => {
+ return ;
+};
+
+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),
+ }),
+};
diff --git a/apps/storybook/src/stories/page-list.stories.tsx b/apps/storybook/src/stories/page-list.stories.tsx
index 116e07e19e..7a9086dee3 100644
--- a/apps/storybook/src/stories/page-list.stories.tsx
+++ b/apps/storybook/src/stories/page-list.stories.tsx
@@ -34,7 +34,7 @@ AffineOperationCell.play = async ({ canvasElement }) => {
'[data-testid="page-list-operation-button"]'
) as HTMLButtonElement;
expect(button).not.toBeNull();
- userEvent.click(button);
+ await userEvent.click(button);
}
};
@@ -51,7 +51,7 @@ AffineNewPageButton.play = async ({ canvasElement }) => {
expect(button).not.toBeNull();
const dropdown = button.querySelector('svg') as SVGSVGElement;
expect(dropdown).not.toBeNull();
- userEvent.click(dropdown);
+ await userEvent.click(dropdown);
};
export const AffineAllPageList: StoryFn = ({ ...props }) => (
@@ -69,11 +69,11 @@ AffineAllPageList.args = {
favorite: false,
icon: ,
isPublicPage: true,
- title: 'Today Page',
+ title: 'Last Page',
tags: [],
preview: 'this is page preview',
- createDate: new Date(),
- updatedDate: new Date(),
+ createDate: new Date('2021-01-01'),
+ updatedDate: new Date('2023-08-15'),
bookmarkPage: () => toast('Bookmark page'),
onClickPage: () => toast('Click page'),
onDisablePublicSharing: () => toast('Disable public sharing'),
diff --git a/apps/storybook/tsconfig.json b/apps/storybook/tsconfig.json
index 5bc6d833a6..c1f080046c 100644
--- a/apps/storybook/tsconfig.json
+++ b/apps/storybook/tsconfig.json
@@ -9,6 +9,9 @@
"outDir": "lib"
},
"references": [
+ {
+ "path": "../../apps/core"
+ },
{
"path": "../../packages/component"
},
diff --git a/apps/storybook/tsconfig.node.json b/apps/storybook/tsconfig.node.json
index b6c1c2b1ac..2c5839ca72 100644
--- a/apps/storybook/tsconfig.node.json
+++ b/apps/storybook/tsconfig.node.json
@@ -4,7 +4,7 @@
"composite": true,
"module": "ESNext",
"jsx": "react-jsx",
- "moduleResolution": "Node",
+ "moduleResolution": "bundler",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"noEmit": false,
@@ -13,6 +13,7 @@
"include": [".storybook/**/*"],
"exclude": ["lib"],
"references": [
+ { "path": "../../apps/core" },
{ "path": "../../packages/i18n" },
{
"path": "../../packages/env"
diff --git a/tsconfig.json b/tsconfig.json
index 83e4fc568b..e1317741ae 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -55,6 +55,7 @@
"skipLibCheck": true, // skip all type checks for .d.ts files
"paths": {
+ "@affine/core/*": ["./packages/core/src/*"],
"@affine/cli/*": ["./packages/cli/src/*"],
"@affine/component": ["./packages/component/src/index"],
"@affine/component/*": [
diff --git a/yarn.lock b/yarn.lock
index 2448ce5d13..2fcf4b5a52 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -709,6 +709,7 @@ __metadata:
react-dom: 18.2.0
serve: ^14.2.0
storybook: ^7.2.3
+ storybook-addon-react-router-v6: ^2.0.4
storybook-dark-mode: ^3.0.1
wait-on: ^7.0.1
peerDependencies:
@@ -15874,6 +15875,13 @@ __metadata:
languageName: node
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":
version: 1.3.0
resolution: "component-emitter@npm:1.3.0"
@@ -27724,7 +27732,7 @@ __metadata:
languageName: node
linkType: hard
-"react-inspector@npm:^6.0.0":
+"react-inspector@npm:6.0.2, react-inspector@npm:^6.0.0":
version: 6.0.2
resolution: "react-inspector@npm:6.0.2"
peerDependencies:
@@ -29775,6 +29783,33 @@ __metadata:
languageName: node
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":
version: 3.0.1
resolution: "storybook-dark-mode@npm:3.0.1"