├── .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 | logo 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 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | --------------------------------------------------------------------------------