From c8af43024622ef6348b461283c30a5a16a2640c3 Mon Sep 17 00:00:00 2001 From: tsiq-swyx Date: Thu, 5 Apr 2018 11:57:34 -0400 Subject: [PATCH 1/3] add typescript docs --- docs/gatsby-config.js | 1 + .../components/Homepage/MainLinks/index.js | 3 + .../custom-webpack-config/index.md | 38 +-- .../configurations/typescript-config/index.md | 229 ++++++++++++++++++ 4 files changed, 253 insertions(+), 18 deletions(-) create mode 100644 docs/src/pages/configurations/typescript-config/index.md diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js index ba05f36ff20f..6b63fa9be767 100644 --- a/docs/gatsby-config.js +++ b/docs/gatsby-config.js @@ -23,6 +23,7 @@ module.exports = { '/configurations/default-config/', '/configurations/custom-webpack-config/', '/configurations/custom-babel-config/', + '/configurations/typescript-config/', '/configurations/add-custom-head-tags/', '/configurations/serving-static-files/', '/configurations/env-vars/', diff --git a/docs/src/components/Homepage/MainLinks/index.js b/docs/src/components/Homepage/MainLinks/index.js index 16b731337b22..d3d028575f81 100644 --- a/docs/src/components/Homepage/MainLinks/index.js +++ b/docs/src/components/Homepage/MainLinks/index.js @@ -102,6 +102,9 @@ class MainLinks extends React.Component {
  • Webpack configurations
  • +
  • + Typescript configurations +
  • Custom scripts & styling
  • diff --git a/docs/src/pages/configurations/custom-webpack-config/index.md b/docs/src/pages/configurations/custom-webpack-config/index.md index d4d9d2e8c704..f5fca70de71b 100644 --- a/docs/src/pages/configurations/custom-webpack-config/index.md +++ b/docs/src/pages/configurations/custom-webpack-config/index.md @@ -19,7 +19,7 @@ Let's say you want to add [SASS](http://sass-lang.com/) support to Storybook. Th Simply add the following content to a file called `webpack.config.js` in your Storybook config directory (`.storybook` by default ). ```js -const path = require('path'); +const path = require("path"); module.exports = { module: { @@ -27,11 +27,11 @@ module.exports = { { test: /\.scss$/, loaders: ["style-loader", "css-loader", "sass-loader"], - include: path.resolve(__dirname, '../') + include: path.resolve(__dirname, "../") } ] } -} +}; ``` Since this config file stays in the Storybook directory, you need to set the include path as above. If the config directory stays in a different directory, you need to set the include path relative to that. @@ -45,9 +45,9 @@ You also need to install the loaders (style, css, sass, as well as node-sass) us You can add any kind of Webpack configuration options with the above config, whether they are plugins, loaders, or aliases. But you won't be able to change the following config options: -- entry -- output -- js loader with babel +* entry +* output +* js loader with babel For the advanced usage we strongly recommend [full control mode](#full-control-mode). @@ -60,7 +60,7 @@ That's where you can use our full control mode. To enable that, you need to export a **function** from the above `webpack.config.js` file, just like this: ```js -const path = require('path'); +const path = require("path"); // Export a function. Accept the base config as the only param. module.exports = (storybookBaseConfig, configType) => { @@ -72,7 +72,7 @@ module.exports = (storybookBaseConfig, configType) => { storybookBaseConfig.module.rules.push({ test: /\.scss$/, loaders: ["style-loader", "css-loader", "sass-loader"], - include: path.resolve(__dirname, '../') + include: path.resolve(__dirname, "../") }); // Return the altered config @@ -82,10 +82,10 @@ module.exports = (storybookBaseConfig, configType) => { Storybook uses the config returned from the above function. So, try to edit the `storybookBaseConfig` with care. Make sure to preserve the following config options: -- entry -- output -- first loader in the module.loaders (Babel loader for JS) -- all existing plugins +* entry +* output +* first loader in the module.loaders (Babel loader for JS) +* all existing plugins > If your custom webpack config uses a loader that does not explicitly include specific file extensions via the `test` property, it is necessary to `exclude` the `.ejs` file extension from that loader. @@ -96,7 +96,7 @@ If so, this is how you do it using the Full Control Mode. Add following content to the `webpack.config.js` in your Storybook config directory. ```js -const path = require('path'); +const path = require("path"); module.exports = (baseConfig, env, defaultConfig) => { // Extend defaultConfig as you need. @@ -104,20 +104,22 @@ module.exports = (baseConfig, env, defaultConfig) => { // For example, add typescript loader: defaultConfig.module.rules.push({ test: /\.(ts|tsx)$/, - include: path.resolve(__dirname, '../src'), - loader: require.resolve('ts-loader') + include: path.resolve(__dirname, "../src"), + loader: require.resolve("ts-loader") }); - defaultConfig.resolve.extensions.push('.ts', '.tsx'); + defaultConfig.resolve.extensions.push(".ts", ".tsx"); return defaultConfig; }; ``` +For full instructions on Typescript setup, check [our dedicated Typescript page](/configurations/typescript-config/). + ## Using Your Existing Config You may have an existing Webpack config for your project. So, you may need to copy and paste some config items into Storybook's custom Webpack config file. But you don't need to. There are a few options: -- Simply import your main Webpack config into Storybook's `webpack.config.js` and use the loaders and plugins used in that. -- Create a new file with common Webpack options and use it in both inside the main Webpack config and inside Storybook's `webpack.config.js`. +* Simply import your main Webpack config into Storybook's `webpack.config.js` and use the loaders and plugins used in that. +* Create a new file with common Webpack options and use it in both inside the main Webpack config and inside Storybook's `webpack.config.js`. diff --git a/docs/src/pages/configurations/typescript-config/index.md b/docs/src/pages/configurations/typescript-config/index.md new file mode 100644 index 000000000000..e4d293316c41 --- /dev/null +++ b/docs/src/pages/configurations/typescript-config/index.md @@ -0,0 +1,229 @@ +--- +id: 'typescript-config' +title: 'Typescript Config' +--- + +This is a central reference for using Storybook with Typescript. + +## Dependencies you may need + +```bash +yarn add -D typescript +yarn add -D awesome-typescript-loader # alternative to ts-loader +yarn add -D @storybook/addon-info react-docgen-typescript-webpack-plugin # optional but recommended +yarn add -D jest "@types/jest" ts-jest #testing +``` + +## Setting up Typescript to work with Storybook + +We first have to use the [custom Webpack config in full control mode, extending default configs](https://storybook.js.org/configurations/custom-webpack-config/#full-control-mode--default): + +```js +// load the default config generator. +const genDefaultConfig = require("@storybook/react/dist/server/config/defaults/webpack.config.js"); +const path = require("path"); +const TSDocgenPlugin = require("react-docgen-typescript-webpack-plugin"); // optional + +module.exports = (baseConfig, env) => { + const config = genDefaultConfig(baseConfig, env); + config.module.rules.push({ + test: /\.(ts|tsx)$/, + include: [ + path.resolve(__dirname, "../src"), + path.resolve(__dirname, "../stories") // e.g. if you have a separate /stories folder + ], + loader: require.resolve("awesome-typescript-loader") // or ts-loader + // alternatively + // loader: 'babel-loader!ts-loader', + }); + config.plugins.push(new TSDocgenPlugin()); // optional + config.resolve.extensions.push(".ts", ".tsx"); + return config; +}; +``` + +The above example shows a working config with the TSDocgen plugin also integrated; remove the optional sections if you don't plan on using them. + +## `tsconfig.json` + +```json +{ + "compilerOptions": { + "outDir": "build/lib", + "module": "commonjs", + "target": "es5", + "lib": ["es5", "es6", "es7", "es2017", "dom"], + "sourceMap": true, + "allowJs": false, + "jsx": "react", + "moduleResolution": "node", + "rootDir": "src", + "baseUrl": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "declaration": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "build", "scripts"] +} +``` + +If you have your stories in a separate `/stories` folder you may wish to replace `rootDir` above with `"rootDirs": ["src", "stories"]`. + +## Using Typescript with the TSDocgen addon + +The very handy [Storybook Info addon](https://github.com/storybooks/storybook/tree/master/addons/info) autogenerates prop tables documentation for each component, however it doesn't work with Typescript types. The current solution is to use [react-docgen-typescript-loader](https://github.com/strothj/react-docgen-typescript-loader) to preprocess the Typescript files to give the Info addon what it needs. The webpack config above does this, and so for the rest of your stories you use it as per normal: + +```js +import React from "react"; +import { storiesOf } from "@storybook/react"; +import { withInfo } from "@storybook/addon-info"; +import { action } from "@storybook/addon-actions"; +import TicTacToeCell from "./TicTacToeCell"; + +const stories = storiesOf("Components", module); + +stories.add( + "TicTacToeCell", + withInfo({ inline: true })(() => ) +); +``` + +## Customizing Type annotations/descriptions + +Please refer to the [react-docgen-typescript-loader](https://github.com/strothj/react-docgen-typescript-loader) docs for writing prop descriptions and other annotations to your Typescript interfaces. + +Additional annotation can be achieved by creating a `wInfo` higher order component: + +```js +import { withInfo } from "@storybook/addon-info"; +const wInfoStyle = { + header: { + h1: { + marginRight: "20px", + fontSize: "25px", + display: "inline" + }, + body: { + paddingTop: 0, + paddingBottom: 0 + }, + h2: { + display: "inline", + color: "#999" + } + }, + infoBody: { + backgroundColor: "#eee", + padding: "0px 5px", + lineHeight: "2" + } +}; +export const wInfo = text => withInfo({ inline: true, source: false, styles: wInfoStyle, text: text }); +``` + +This can be used like so: + +```js +import React from "react"; + +import { storiesOf } from "@storybook/react"; +import { PrimaryButton } from "./Button"; +import { wInfo } from "../../utils"; +import { text, select, boolean } from "@storybook/addon-knobs/react"; + +storiesOf("Components/Button", module).addWithJSX( + "basic PrimaryButton", + wInfo(` + + ### Notes + + light button seen on + + ### Usage + ~~~js + alert('hello there')} + /> + ~~~ + +`)(() => ( + alert("hello there")} + /> + )) +); +``` + +And this is how it looks: + +![image](https://user-images.githubusercontent.com/35976578/38376038-ac02b432-38c5-11e8-9aed-f4fa2e258f60.png) + +Note: Component docgen information can not be generated for components that are only exported as default. You can work around the issue by exporting the component using a named export. + +## Setting up Jest tests + +The ts-jest [README](https://github.com/kulshekhar/ts-jest) explains pretty clearly how to get Jest to recognize TypeScript code. + +This is an example package.json config for jest: + +```json +"jest": { + "transform": { + ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" + }, + "mapCoverage": true, + "testPathIgnorePatterns": [ + "/node_modules/", + "/lib/" + ], + "testRegex": "(/test/.*|\\.(test|spec))\\.(ts|tsx|js)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ] +} +``` + +## Building your Typescript Storybook + +You will need to set up some scripts - these may help: + +```json + "scripts": { + "start": "react-scripts-ts start", + "build": "npm run lint && npm run build-lib && build-storybook", + "build-lib-watch": "tsc -w", + "build-lib": "tsc && npm run copy-css-to-lib && npm run copy-svg-to-lib && npm run copy-png-to-lib && npm run copy-woff2-to-lib", + "test": "react-scripts-ts test --env=jsdom", + "test:coverage": "npm test -- --coverage", + "eject": "react-scripts-ts eject", + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook", + "copy-css-to-lib": "cpx \"./src/**/*.css\" ./build/lib", + "copy-woff2-to-lib": "cpx \"./src/**/*.woff2\" ./build/lib", + "copy-svg-to-lib": "cpx \"./src/**/*.svg\" ./build/lib", + "copy-png-to-lib": "cpx \"./src/**/*.png\" ./build/lib", + "lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}'" + }, +``` + +## Related Issues and Helpful Resources + +* [Issue: Add proper typescript webpack setup in readme #1778](https://github.com/storybooks/storybook/issues/1778) +* [Issue: Addons info and Typescript props docgen? #1519](https://github.com/storybooks/storybook/issues/1519) +* [Storybook, React, TypeScript and Jest](https://medium.com/@mtiller/storybook-react-typescript-and-jest-c9059ea06fa7) +* [React, Storybook & TypeScript](http://www.joshschreuder.me/react-storybooks-with-typescript/) From 7d6e09911dafc38537898cd29ba63eebb292f0e2 Mon Sep 17 00:00:00 2001 From: tsiq-swyx Date: Thu, 5 Apr 2018 12:54:03 -0400 Subject: [PATCH 2/3] incorporate danielduan comments --- .../src/pages/configurations/custom-webpack-config/index.md | 4 ++-- docs/src/pages/configurations/typescript-config/index.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/pages/configurations/custom-webpack-config/index.md b/docs/src/pages/configurations/custom-webpack-config/index.md index f5fca70de71b..fe5ef3d0b5ac 100644 --- a/docs/src/pages/configurations/custom-webpack-config/index.md +++ b/docs/src/pages/configurations/custom-webpack-config/index.md @@ -16,7 +16,7 @@ There are a few ways to do it: You'll get _extend-mode_ by returning an object. Let's say you want to add [SASS](http://sass-lang.com/) support to Storybook. This is how to do it. -Simply add the following content to a file called `webpack.config.js` in your Storybook config directory (`.storybook` by default ). +Add the following content to a file called `webpack.config.js` in your Storybook config directory (`.storybook` by default ). ```js const path = require("path"); @@ -121,5 +121,5 @@ You may have an existing Webpack config for your project. So, you may need to co But you don't need to. There are a few options: -* Simply import your main Webpack config into Storybook's `webpack.config.js` and use the loaders and plugins used in that. +* Import your main Webpack config into Storybook's `webpack.config.js` and use the loaders and plugins used in that. * Create a new file with common Webpack options and use it in both inside the main Webpack config and inside Storybook's `webpack.config.js`. diff --git a/docs/src/pages/configurations/typescript-config/index.md b/docs/src/pages/configurations/typescript-config/index.md index e4d293316c41..846e3aed77cc 100644 --- a/docs/src/pages/configurations/typescript-config/index.md +++ b/docs/src/pages/configurations/typescript-config/index.md @@ -14,9 +14,11 @@ yarn add -D @storybook/addon-info react-docgen-typescript-webpack-plugin # optio yarn add -D jest "@types/jest" ts-jest #testing ``` +We have no opinion on whether you use `awesome-typescript-loader` or `ts-loader`, just configure either accordingly. You can even use `babel-loader` with a `ts-loader` configuration. + ## Setting up Typescript to work with Storybook -We first have to use the [custom Webpack config in full control mode, extending default configs](https://storybook.js.org/configurations/custom-webpack-config/#full-control-mode--default): +We first have to use the [custom Webpack config in full control mode, extending default configs](/configurations/custom-webpack-config/#full-control-mode--default): ```js // load the default config generator. @@ -223,7 +225,5 @@ You will need to set up some scripts - these may help: ## Related Issues and Helpful Resources -* [Issue: Add proper typescript webpack setup in readme #1778](https://github.com/storybooks/storybook/issues/1778) -* [Issue: Addons info and Typescript props docgen? #1519](https://github.com/storybooks/storybook/issues/1519) * [Storybook, React, TypeScript and Jest](https://medium.com/@mtiller/storybook-react-typescript-and-jest-c9059ea06fa7) * [React, Storybook & TypeScript](http://www.joshschreuder.me/react-storybooks-with-typescript/) From 0933c137dc2523516747aceeec9fa234f88db685 Mon Sep 17 00:00:00 2001 From: tsiq-swyx Date: Thu, 5 Apr 2018 14:12:57 -0400 Subject: [PATCH 3/3] add ed staub fixes --- .../configurations/typescript-config/index.md | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/docs/src/pages/configurations/typescript-config/index.md b/docs/src/pages/configurations/typescript-config/index.md index 846e3aed77cc..83365957de0c 100644 --- a/docs/src/pages/configurations/typescript-config/index.md +++ b/docs/src/pages/configurations/typescript-config/index.md @@ -9,34 +9,24 @@ This is a central reference for using Storybook with Typescript. ```bash yarn add -D typescript -yarn add -D awesome-typescript-loader # alternative to ts-loader +yarn add -D awesome-typescript-loader yarn add -D @storybook/addon-info react-docgen-typescript-webpack-plugin # optional but recommended yarn add -D jest "@types/jest" ts-jest #testing ``` -We have no opinion on whether you use `awesome-typescript-loader` or `ts-loader`, just configure either accordingly. You can even use `babel-loader` with a `ts-loader` configuration. +We have had the best experience using `awesome-typescript-loader`, but other tutorials may use `ts-loader`, just configure accordingly. You can even use `babel-loader` with a `ts-loader` configuration. ## Setting up Typescript to work with Storybook We first have to use the [custom Webpack config in full control mode, extending default configs](/configurations/custom-webpack-config/#full-control-mode--default): ```js -// load the default config generator. -const genDefaultConfig = require("@storybook/react/dist/server/config/defaults/webpack.config.js"); const path = require("path"); -const TSDocgenPlugin = require("react-docgen-typescript-webpack-plugin"); // optional - -module.exports = (baseConfig, env) => { - const config = genDefaultConfig(baseConfig, env); +const TSDocgenPlugin = require("react-docgen-typescript-webpack-plugin"); +module.exports = (baseConfig, env, config) => { config.module.rules.push({ test: /\.(ts|tsx)$/, - include: [ - path.resolve(__dirname, "../src"), - path.resolve(__dirname, "../stories") // e.g. if you have a separate /stories folder - ], - loader: require.resolve("awesome-typescript-loader") // or ts-loader - // alternatively - // loader: 'babel-loader!ts-loader', + loader: require.resolve("awesome-typescript-loader") }); config.plugins.push(new TSDocgenPlugin()); // optional config.resolve.extensions.push(".ts", ".tsx"); @@ -59,7 +49,7 @@ The above example shows a working config with the TSDocgen plugin also integrate "allowJs": false, "jsx": "react", "moduleResolution": "node", - "rootDir": "src", + "rootDirs": ["src", "stories"], "baseUrl": "src", "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, @@ -77,7 +67,7 @@ The above example shows a working config with the TSDocgen plugin also integrate } ``` -If you have your stories in a separate `/stories` folder you may wish to replace `rootDir` above with `"rootDirs": ["src", "stories"]`. +This is for the default configuration where `/stories` is a peer of `src`. If you have them all in just `src` you may wish to replace `"rootDirs": ["src", "stories"]` above with `"rootDir": "src",`. ## Using Typescript with the TSDocgen addon