feat(storybook): preview app (#3765)

This commit is contained in:
Alex Yang
2023-08-15 15:34:02 -05:00
committed by GitHub
parent 42dfd0a4bb
commit 67b33d9b8f
15 changed files with 179 additions and 76 deletions

View File

@@ -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

View File

@@ -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:*",

View File

@@ -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<

View File

@@ -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',
}, },

View File

@@ -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,
},
});

View File

@@ -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": [
{ {

View File

@@ -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',

View File

@@ -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>
); );
}, },

View File

@@ -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": "*",

View 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),
}),
};

View File

@@ -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'),

View File

@@ -9,6 +9,9 @@
"outDir": "lib" "outDir": "lib"
}, },
"references": [ "references": [
{
"path": "../../apps/core"
},
{ {
"path": "../../packages/component" "path": "../../packages/component"
}, },

View File

@@ -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"

View File

@@ -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/*": [

View File

@@ -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"