Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add typescript docs #3361

Merged
merged 4 commits into from
Apr 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
Expand Down
3 changes: 3 additions & 0 deletions docs/src/components/Homepage/MainLinks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ class MainLinks extends React.Component {
<li>
<Link to="/configurations/custom-webpack-config/">Webpack configurations</Link>
</li>
<li>
<Link to="/configurations/typescript-config/">Typescript configurations</Link>
</li>
<li>
<Link to="/configurations/add-custom-head-tags/">Custom scripts & styling</Link>
</li>
Expand Down
40 changes: 21 additions & 19 deletions docs/src/pages/configurations/custom-webpack-config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ 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');
const path = require("path");

module.exports = {
module: {
rules: [
{
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.
Expand All @@ -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).

Expand All @@ -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) => {
Expand All @@ -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
Expand All @@ -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.

Expand All @@ -96,28 +96,30 @@ 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.

// 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`.
* 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`.
219 changes: 219 additions & 0 deletions docs/src/pages/configurations/typescript-config/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
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
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 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
const path = require("path");
const TSDocgenPlugin = require("react-docgen-typescript-webpack-plugin");
module.exports = (baseConfig, env, config) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve("awesome-typescript-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",
"rootDirs": ["src", "stories"],
"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"]
}
```

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

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 })(() => <TicTacToeCell value="X" position={{ x: 0, y: 0 }} onClick={action("onClick")} />)
);
```

## 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 <https://zpl.io/aM49ZBd>

### Usage
~~~js
<PrimaryButton
label={text('label', 'Enroll')}
disabled={boolean('disabled',false)}
onClick={() => alert('hello there')}
/>
~~~

`)(() => (
<PrimaryButton
label={text("label", "Enroll")}
disabled={boolean("disabled", false)}
onClick={() => 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)": "<rootDir>/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

* [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/)