├── .gitignore
├── .storybook
├── addons.js
├── config.js
└── webpack.config.js
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.css
├── App.test.tsx
├── App.tsx
├── decorators
│ └── CenterDecorator.tsx
├── index.css
├── index.tsx
├── logo.svg
└── registerServiceWorker.ts
├── stories
└── index.tsx
├── tsconfig.json
├── tsconfig.test.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/.storybook/addons.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
2 |
3 | import '@storybook/addon-actions/register';
4 | import '@storybook/addon-links/register';
5 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
2 |
3 | import { configure } from '@storybook/react';
4 |
5 | const req = require.context('../stories', true, /\.tsx?$/)
6 |
7 | function loadStories() {
8 | req.keys().forEach((filename) => req(filename))
9 | }
10 |
11 | configure(loadStories, module);
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const include = path.resolve(__dirname, '../');
3 |
4 | module.exports = {
5 | // Add '.ts' and '.tsx' as resolvable extensions.
6 | resolve: {
7 | extensions: [".ts", ".tsx", ".js"]
8 | },
9 | module: {
10 | rules: [
11 | {
12 | test: /\.tsx/,
13 | loader: 'babel-loader!ts-loader',
14 | exclude: /node_modules/,
15 | include
16 | }
17 | ]
18 | }
19 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Storybook 3 TypeScript example
2 |
3 | A sample repo showing how I got Storybook 3 up and running with TypeScript.
4 |
5 | I detailed my process [on my blog](http://www.joshschreuder.me/react-storybooks-with-typescript/), beginning with `create-react-app` and `storybook`. Everything here is untouched from a default `storybook` installation, apart from converting what is generated by `getstorybook` to TypeScript.
6 |
7 | ### Getting started
8 | `yarn install` or `npm install`
9 |
10 | `yarn run storybook` or `npm run storybook`
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "storybook3-ts-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/jest": "^20.0.6",
7 | "@types/node": "^8.0.20",
8 | "@types/react": "^16.0.2",
9 | "@types/react-dom": "^15.5.2",
10 | "@types/storybook__addon-actions": "^3.0.1",
11 | "@types/storybook__addon-links": "^3.0.1",
12 | "@types/storybook__react": "^3.0.3",
13 | "react": "^15.6.1",
14 | "react-dom": "^15.6.1",
15 | "react-scripts-ts": "2.6.0"
16 | },
17 | "scripts": {
18 | "start": "react-scripts-ts start",
19 | "build": "react-scripts-ts build",
20 | "test": "react-scripts-ts test --env=jsdom",
21 | "eject": "react-scripts-ts eject",
22 | "storybook": "start-storybook -p 6006",
23 | "build-storybook": "build-storybook"
24 | },
25 | "devDependencies": {
26 | "@storybook/react": "^3.2.3"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoshSchreuder/storybook3-typescript-example/c9f80550c971ea974935bbc776d4fc21e784b5bf/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | @keyframes App-logo-spin {
22 | from { transform: rotate(0deg); }
23 | to { transform: rotate(360deg); }
24 | }
25 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | });
9 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import './App.css';
3 |
4 | const logo = require('./logo.svg');
5 |
6 | class App extends React.Component {
7 | render() {
8 | return (
9 |
10 |
11 |

12 |
Welcome to React
13 |
14 |
15 | To get started, edit src/App.tsx
and save to reload.
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/src/decorators/CenterDecorator.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { RenderFunction } from '@storybook/react';
4 |
5 | export const CenterDecorator = (story: RenderFunction) => (
6 |
7 | {story()}
8 |
9 | );
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 | import './index.css';
6 |
7 | ReactDOM.render(
8 | ,
9 | document.getElementById('root') as HTMLElement
10 | );
11 | registerServiceWorker();
12 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable:no-console
2 | // In production, we register a service worker to serve assets from local cache.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on the 'N+1' visit to a page, since previously
7 | // cached resources are updated in the background.
8 |
9 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
10 | // This link also includes instructions on opting out of this behavior.
11 |
12 | const isLocalhost = Boolean(
13 | window.location.hostname === 'localhost' ||
14 | // [::1] is the IPv6 localhost address.
15 | window.location.hostname === '[::1]' ||
16 | // 127.0.0.1/8 is considered localhost for IPv4.
17 | window.location.hostname.match(
18 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
19 | )
20 | );
21 |
22 | export default function register() {
23 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
24 | // The URL constructor is available in all browsers that support SW.
25 | const publicUrl = new URL(
26 | process.env.PUBLIC_URL!,
27 | window.location.toString()
28 | );
29 | if (publicUrl.origin !== window.location.origin) {
30 | // Our service worker won't work if PUBLIC_URL is on a different origin
31 | // from what our page is served on. This might happen if a CDN is used to
32 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
33 | return;
34 | }
35 |
36 | window.addEventListener('load', () => {
37 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
38 |
39 | if (!isLocalhost) {
40 | // Is not local host. Just register service worker
41 | registerValidSW(swUrl);
42 | } else {
43 | // This is running on localhost. Lets check if a service worker still exists or not.
44 | checkValidServiceWorker(swUrl);
45 | }
46 | });
47 | }
48 | }
49 |
50 | function registerValidSW(swUrl: string) {
51 | navigator.serviceWorker
52 | .register(swUrl)
53 | .then(registration => {
54 | registration.onupdatefound = () => {
55 | const installingWorker = registration.installing;
56 | if (installingWorker) {
57 | installingWorker.onstatechange = () => {
58 | if (installingWorker.state === 'installed') {
59 | if (navigator.serviceWorker.controller) {
60 | // At this point, the old content will have been purged and
61 | // the fresh content will have been added to the cache.
62 | // It's the perfect time to display a 'New content is
63 | // available; please refresh.' message in your web app.
64 | console.log('New content is available; please refresh.');
65 | } else {
66 | // At this point, everything has been precached.
67 | // It's the perfect time to display a
68 | // 'Content is cached for offline use.' message.
69 | console.log('Content is cached for offline use.');
70 | }
71 | }
72 | };
73 | }
74 | };
75 | })
76 | .catch(error => {
77 | console.error('Error during service worker registration:', error);
78 | });
79 | }
80 |
81 | function checkValidServiceWorker(swUrl: string) {
82 | // Check if the service worker can be found. If it can't reload the page.
83 | fetch(swUrl)
84 | .then(response => {
85 | // Ensure service worker exists, and that we really are getting a JS file.
86 | if (
87 | response.status === 404 ||
88 | response.headers.get('content-type')!.indexOf('javascript') === -1
89 | ) {
90 | // No service worker found. Probably a different app. Reload the page.
91 | navigator.serviceWorker.ready.then(registration => {
92 | registration.unregister().then(() => {
93 | window.location.reload();
94 | });
95 | });
96 | } else {
97 | // Service worker found. Proceed as normal.
98 | registerValidSW(swUrl);
99 | }
100 | })
101 | .catch(() => {
102 | console.log(
103 | 'No internet connection found. App is running in offline mode.'
104 | );
105 | });
106 | }
107 |
108 | export function unregister() {
109 | if ('serviceWorker' in navigator) {
110 | navigator.serviceWorker.ready.then(registration => {
111 | registration.unregister();
112 | });
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/stories/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { storiesOf } from '@storybook/react';
4 | import { action } from '@storybook/addon-actions';
5 | import { linkTo } from '@storybook/addon-links';
6 |
7 | import { CenterDecorator } from '../src/decorators/CenterDecorator';
8 |
9 | const { Button, Welcome } = require('@storybook/react/demo');
10 |
11 | storiesOf('Welcome', module).add('to Storybook', () => );
12 |
13 | storiesOf('Button', module)
14 | .addDecorator(CenterDecorator)
15 | .add('with text', () => )
16 | .add('with some emoji', () => );
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "build/dist",
4 | "module": "esnext",
5 | "target": "es5",
6 | "lib": ["es6", "dom"],
7 | "sourceMap": true,
8 | "allowJs": true,
9 | "jsx": "react",
10 | "moduleResolution": "node",
11 | "rootDirs": ["src", "stories"],
12 | "forceConsistentCasingInFileNames": true,
13 | "noImplicitReturns": true,
14 | "noImplicitThis": true,
15 | "noImplicitAny": true,
16 | "strictNullChecks": true,
17 | "suppressImplicitAnyIndexErrors": true,
18 | "noUnusedLocals": true
19 | },
20 | "exclude": [
21 | "node_modules",
22 | "build",
23 | "scripts",
24 | "acceptance-tests",
25 | "webpack",
26 | "jest",
27 | "src/setupTests.ts"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-react"],
3 | "rules": {
4 | "align": [
5 | true,
6 | "parameters",
7 | "arguments",
8 | "statements"
9 | ],
10 | "ban": false,
11 | "class-name": true,
12 | "comment-format": [
13 | true,
14 | "check-space"
15 | ],
16 | "curly": true,
17 | "eofline": false,
18 | "forin": true,
19 | "indent": [ true, "spaces" ],
20 | "interface-name": [true, "never-prefix"],
21 | "jsdoc-format": true,
22 | "jsx-no-lambda": false,
23 | "jsx-no-multiline-js": false,
24 | "label-position": true,
25 | "max-line-length": [ true, 120 ],
26 | "member-ordering": [
27 | true,
28 | "public-before-private",
29 | "static-before-instance",
30 | "variables-before-functions"
31 | ],
32 | "no-any": true,
33 | "no-arg": true,
34 | "no-bitwise": true,
35 | "no-console": [
36 | true,
37 | "log",
38 | "error",
39 | "debug",
40 | "info",
41 | "time",
42 | "timeEnd",
43 | "trace"
44 | ],
45 | "no-consecutive-blank-lines": true,
46 | "no-construct": true,
47 | "no-debugger": true,
48 | "no-duplicate-variable": true,
49 | "no-empty": true,
50 | "no-eval": true,
51 | "no-shadowed-variable": true,
52 | "no-string-literal": true,
53 | "no-switch-case-fall-through": true,
54 | "no-trailing-whitespace": false,
55 | "no-unused-expression": true,
56 | "no-use-before-declare": true,
57 | "one-line": [
58 | true,
59 | "check-catch",
60 | "check-else",
61 | "check-open-brace",
62 | "check-whitespace"
63 | ],
64 | "quotemark": [true, "single", "jsx-double"],
65 | "radix": true,
66 | "semicolon": [true, "always"],
67 | "switch-default": true,
68 |
69 | "trailing-comma": false,
70 |
71 | "triple-equals": [ true, "allow-null-check" ],
72 | "typedef": [
73 | true,
74 | "parameter",
75 | "property-declaration"
76 | ],
77 | "typedef-whitespace": [
78 | true,
79 | {
80 | "call-signature": "nospace",
81 | "index-signature": "nospace",
82 | "parameter": "nospace",
83 | "property-declaration": "nospace",
84 | "variable-declaration": "nospace"
85 | }
86 | ],
87 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
88 | "whitespace": [
89 | true,
90 | "check-branch",
91 | "check-decl",
92 | "check-module",
93 | "check-operator",
94 | "check-separator",
95 | "check-type",
96 | "check-typecast"
97 | ]
98 | }
99 | }
100 |
--------------------------------------------------------------------------------