What is Storybook and Why do we Use It?
Storybook is UI to explore and design components in our project. Using it, developers can browse available components.
Frontend engineers can design components UI and behavior using Storybook, independently of the page or data source logic. This makes components easier to develop and test.
How to Use Storybook to Explore React Components
Lovia PWA project should have Storybook already set up. You can check this if the project has a .storybook/
folder. Launch Storybook by:
yarn storybook
Open http://localhost:6006/ . Now you can explore and play with components.
Adding/Editing Stories
Add or edit stories in src/stories/*.stories.tsx
(in a project, these stories files can be anywhere)
Advanced: Setting Up Storybook
# Create an UmiJS project with antd
mkdir ant-storybook-v6
cd ant-storybook-v6
yarn create umi
# Choose app
# Choose antd + internationalization
# Add typescript, so storybook will detect it as TypeScript project instead of JavaScript project
yarn add --dev typescript
# Install storybook
npx sb init
# Start storybook
yarn storybook
Open http://localhost:6006/
Note: Storybook vs Bit.dev as component explorer? Bit seems to be more invasive, and opinionated while not entirely inline with what we want. Storybook is simpler to get started, can integrate with existing project, and has useful addons beyond just UI components.
Advanced: Storybook and Ant Design Pro
Due to how Ant Design Pro / UmiJS project is structured, the less-loader, and how Babel & webpack is configured, Storybook does not work out-of-the-box with Ant Design/Pro components.
TL;DR: Install package @storybook/preset-ant-design. Then modify .storybook/main.js
like this:
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-ant-design'],
webpackFinal: async config => ({
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'@@': path.resolve(__dirname, '../src/.umi'),
}
}
}),
};
References:
- https://www.npmjs.com/package/@storybook/preset-ant-design
- https://stackoverflow.com/questions/64895364/storybook-with-ant-design-theme-less-not-working
- https://github.com/ant-design/ant-design/issues/27414
- https://github.com/ant-design/ant-design-pro/issues/6858
- https://github.com/storybookjs/storybook/issues/11255
yarn add --dev @storybook/preset-ant-design
Then add the following to .storybook/main.js
:
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-ant-design'],
};
Problem:
When doing import { useIntl } from 'umi'
:
ERROR in ./node_modules/umi/dist/index.esm.js
Module not found: Error: Can't resolve '@@/core/umiExports' in '/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/dist'
@ ./node_modules/umi/dist/index.esm.js 2:0-35 2:0-35
@ ./src/components/CampaignList/index.tsx
@ ./src/stories/CampaignList.stories.tsx
@ ./src sync ^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(js|jsx|ts|tsx))$
@ ./.storybook/generated-stories-entry.js
@ multi ./node_modules/@storybook/core-client/dist/esm/globals/polyfills.js ./node_modules/@storybook/core-client/dist/esm/globals/globals.js ./.storybook/storybook-init-framework-entry.js ./node_modules/@storybook/addon-docs/dist/esm/frameworks/common/config.js-generated-other-entry.js ./node_modules/@storybook/addon-docs/dist/esm/frameworks/react/config.js-generated-other-entry.js ./node_modules/@storybook/addon-links/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/esm/preset/addArgs.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/esm/preset/addParameter.js-generated-other-entry.js ./node_modules/@storybook/addon-measure/dist/esm/preset/preview.js-generated-other-entry.js ./node_modules/storybook-addon-outline/dist/esm/preset/addDecorator.js-generated-other-entry.js ./.storybook/preview.js-generated-config-entry.js ./.storybook/generated-stories-entry.js (webpack)-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined
ERROR in chunk vendors~main [initial]
vendors~main.iframe.bundle.js
/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/dist/index.esm.js
Cannot read property 'isProvided' of null
Child HtmlWebpackCompiler:
Asset Size Chunks Chunk Names
__child-HtmlWebpackPlugin_0 6.23 KiB HtmlWebpackPlugin_0 HtmlWebpackPlugin_0
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/@storybook/core-common/dist/cjs/templates/index.ejs] 1.98 KiB {HtmlWebpackPlugin_0} [built]
ModuleNotFoundError: Module not found: Error: Can't resolve '@@/core/umiExports' in '/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/dist'
at /home/ceefour/project/tmra/tmra-pwa/node_modules/webpack/lib/Compilation.js:925:10
at /home/ceefour/project/tmra/tmra-pwa/node_modules/webpack/lib/NormalModuleFactory.js:401:22
at /home/ceefour/project/tmra/tmra-pwa/node_modules/webpack/lib/NormalModuleFactory.js:130:21
at /home/ceefour/project/tmra/tmra-pwa/node_modules/webpack/lib/NormalModuleFactory.js:224:22
at /home/ceefour/project/tmra/tmra-pwa/node_modules/neo-async/async.js:2830:7
at /home/ceefour/project/tmra/tmra-pwa/node_modules/neo-async/async.js:6877:13
at /home/ceefour/project/tmra/tmra-pwa/node_modules/webpack/lib/NormalModuleFactory.js:214:25
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/Resolver.js:213:14
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/Resolver.js:285:5
at eval (eval at create (/home/ceefour/project/tmra/tmra-pwa/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:44:7
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/Resolver.js:285:5
at eval (eval at create (/home/ceefour/project/tmra/tmra-pwa/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/Resolver.js:285:5
at eval (eval at create (/home/ceefour/project/tmra/tmra-pwa/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
at /home/ceefour/project/tmra/tmra-pwa/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:67:43
resolve '@@/core/umiExports' in '/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/dist'
Parsed request is a module
using description file: /home/ceefour/project/tmra/tmra-pwa/node_modules/umi/package.json (relative path: ./dist)
Field 'browser' doesn't contain a valid alias configuration
resolve as module
/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/dist/node_modules doesn't exist or is not a directory
/home/ceefour/project/tmra/tmra-pwa/node_modules/node_modules doesn't exist or is not a directory
/home/ceefour/project/tmra/node_modules doesn't exist or is not a directory
/home/ceefour/project/node_modules doesn't exist or is not a directory
/home/ceefour/node_modules doesn't exist or is not a directory
/home/node_modules doesn't exist or is not a directory
/node_modules doesn't exist or is not a directory
looking for modules in /home/ceefour/project/tmra/tmra-pwa/node_modules/umi/node_modules
using description file: /home/ceefour/project/tmra/tmra-pwa/node_modules/umi/package.json (relative path: ./node_modules)
Field 'browser' doesn't contain a valid alias configuration
looking for modules in /home/ceefour/project/tmra/tmra-pwa/node_modules
using description file: /home/ceefour/project/tmra/tmra-pwa/package.json (relative path: ./node_modules)
Field 'browser' doesn't contain a valid alias configuration
using description file: /home/ceefour/project/tmra/tmra-pwa/node_modules/umi/package.json (relative path: ./node_modules/@@/core/umiExports)
no extension
Field 'browser' doesn't contain a valid alias configuration
using description file: /home/ceefour/project/tmra/tmra-pwa/package.json (relative path: ./node_modules/@@/core/umiExports)
no extension
Field 'browser' doesn't contain a valid alias configuration
/home/ceefour/project/tmra/tmra-pwa/node_modules/umi/node_modules/@@/core/umiExports doesn't exist
.mjs
Field 'browser' doesn't contain a valid alias configuration
/home/ceefour/project/tmra/tmra-pwa/node_modules/@@/core/umiExports doesn't exist
Clues:
Solution:
const path = require('path');
module.exports = {
// ...
webpackFinal: async config => ({
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'@@': path.resolve(__dirname, '../src/.umi'),
}
}
}),
};