├── .gitignore ├── .storybook ├── main.js ├── next-preset.js └── preview.js ├── README.md ├── babel.config.json ├── components ├── Button.stories.tsx ├── Button.tsx └── __test__ │ ├── Button.snapshot.test.tsx │ ├── __snapshots__ │ └── Button.snapshot.test.tsx.snap │ └── index.test.tsx ├── config └── setup.js ├── jest.config.js ├── next-env.d.ts ├── package-lock.json ├── package.json ├── pages ├── api │ └── hello.js └── index.tsx ├── public ├── favicon.ico └── vercel.svg ├── styles └── global.scss ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | stories: ["../components/**/*.stories.tsx"], 5 | // Add nextjs preset 6 | presets: [path.resolve(__dirname, "./next-preset.js")], 7 | }; 8 | -------------------------------------------------------------------------------- /.storybook/next-preset.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | webpackFinal: async (baseConfig, options) => { 5 | const { module = {} } = baseConfig; 6 | 7 | const newConfig = { 8 | ...baseConfig, 9 | module: { 10 | ...module, 11 | rules: [...(module.rules || [])], 12 | }, 13 | }; 14 | 15 | // TypeScript with Next.js 16 | newConfig.module.rules.push({ 17 | test: /\.(ts|tsx)$/, 18 | include: [path.resolve(__dirname, "../components")], 19 | use: [ 20 | { 21 | loader: "babel-loader", 22 | options: { 23 | presets: ["next/babel", require.resolve("babel-preset-react-app")], 24 | plugins: ["react-docgen"], 25 | }, 26 | }, 27 | ], 28 | }); 29 | newConfig.resolve.extensions.push(".ts", ".tsx"); 30 | 31 | // SCSS preset for Storybook 32 | newConfig.module.rules.push({ 33 | test: /\.(s*)css$/, 34 | loaders: ["style-loader", "css-loader", "sass-loader"], 35 | include: path.resolve(__dirname, "../styles/global.scss"), 36 | }); 37 | 38 | return newConfig; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | // Import global css here 2 | import "../styles/global.scss"; 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 |

Template for Storybook, Next, Typescript, SCSS and Jest

4 | 5 |

6 | 7 | PRs Welcome 8 | 9 | 10 | Liscence 11 | 12 | 13 |

14 | 15 | This simple guide will help you to set up `Storybook`, `Next`, `Typescript`, `SCSS` and `Jest`. 16 | 17 | You can find the full tutorial on [Medium](https://medium.com/@trinwin/2020-complete-setup-for-storybook-nextjs-typescript-scss-and-jest-1c9ce41e6481) 📕 or [DEV.to](https://dev.to/trinwin/2020-complete-setup-for-storybook-nextjs-typescript-scss-and-jest-4khm) 18 | 19 | ## ✅ Getting Started 20 | 21 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 22 | 23 | ### ⚒️ Installation 24 | 25 | ``` 26 | # Clone this repository 27 | git clone https://github.com/trinwin/storybook-next-ts-template 28 | 29 | # Go into the repository 30 | cd storybook-next-ts-template 31 | 32 | # Install client dependencies 33 | yarn install 34 | 35 | # Start client on localhost:3000 36 | yarn run dev 37 | 38 | # Start Storybook 39 | yarn storybook 40 | 41 | # Run your test 42 | yarn test 43 | ``` 44 | 45 | ## ⭐️ Author 46 | 47 | 👩🏻‍💻 **Trinity Nguyen** - [trinwin](https://github.com/trinwin) 48 | 49 | ➡️ I will continue to update and make changes to the tutorial. 50 | 51 | 🤙🏻 Please let me know if you run into any problem. 52 | 53 | 🤝 Connect with me and find my works on: 54 | 55 | - [Twitter](https://twitter.com/_trinwin) 56 | - [Medium](https://medium.com/@trinwin) 57 | - [DEV.to](https://dev.to/trinwin) 58 | - [LinkedIn](https://www.linkedin.com/in/trinwin) 59 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"], 3 | "plugins": [ 4 | "@babel/plugin-transform-runtime", 5 | "@babel/plugin-syntax-dynamic-import", 6 | "@babel/plugin-transform-modules-commonjs", 7 | "@babel/plugin-proposal-class-properties" 8 | ], 9 | "env": { 10 | "development": { 11 | "plugins": ["transform-es2015-modules-commonjs"] 12 | }, 13 | "test": { 14 | "plugins": [ 15 | "transform-es2015-modules-commonjs", 16 | "@babel/plugin-proposal-class-properties" 17 | ], 18 | "presets": ["@babel/preset-react"] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /components/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import Button from "./Button"; 3 | 4 | storiesOf("Button", module).add("with text", () => { 5 | return ; 8 | -------------------------------------------------------------------------------- /components/__test__/Button.snapshot.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Button from "../Button"; 3 | import renderer from "react-test-renderer"; 4 | 5 | it("renders correctly", () => { 6 | const tree = renderer.create( 7 | `; 8 | -------------------------------------------------------------------------------- /components/__test__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { mount } from "enzyme"; 3 | import Home from "../../pages/index"; 4 | describe("Pages", () => { 5 | describe("Home", () => { 6 | it("should render without throwing an error", function () { 7 | const wrap = mount(); 8 | expect(wrap.find("h1").text()).toBe("Welcome to My Next App!"); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /config/setup.js: -------------------------------------------------------------------------------- 1 | const enzyme = require("enzyme"); 2 | const Adapter = require("enzyme-adapter-react-16"); 3 | 4 | enzyme.configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: [ 3 | "**/*.{ts,tsx}", 4 | "!**/node_modules/**", 5 | "!**/.storybook/**", 6 | "!**/tests/**", 7 | "!**/coverage/**", 8 | "!jest.config.js", 9 | ], 10 | coverageThreshold: { 11 | global: { 12 | branches: 100, 13 | functions: 100, 14 | lines: 100, 15 | statements: 100, 16 | }, 17 | }, 18 | setupFiles: ["/config/setup.js"], 19 | preset: "ts-jest", 20 | testPathIgnorePatterns: [ 21 | "/.next/", 22 | "/node_modules/", 23 | "/lib/", 24 | "/tests/", 25 | "/coverage/", 26 | "/.storybook/", 27 | ], 28 | testRegex: "(/__test__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 29 | testURL: "http://localhost", 30 | testEnvironment: "jsdom", 31 | moduleFileExtensions: ["ts", "tsx", "js", "json"], 32 | moduleNameMapper: { 33 | "\\.(css|less)$": "/__mocks__/styleMock.js", 34 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": 35 | "/__mocks__/fileMock.js", 36 | }, 37 | transform: { 38 | ".(ts|tsx)": "babel-jest", 39 | }, 40 | transformIgnorePatterns: ["/node_modules/"], 41 | }; 42 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "storybook": "start-storybook -p 6006 -c .storybook", 10 | "test": "jest", 11 | "test:watch": "jest --watch", 12 | "test:coverage": "jest --coverage" 13 | }, 14 | "dependencies": { 15 | "next": "9.5.4", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "sass": "^1.26.10" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.11.0", 22 | "@babel/plugin-transform-runtime": "^7.11.0", 23 | "@babel/preset-env": "^7.11.0", 24 | "@babel/preset-flow": "^7.10.4", 25 | "@babel/preset-react": "^7.10.4", 26 | "@storybook/react": "^6.0.27", 27 | "@types/enzyme": "^3.10.5", 28 | "@types/jest": "^26.0.8", 29 | "@types/node": "^14.0.27", 30 | "@types/react": "^16.9.44", 31 | "babel-jest": "^26.2.2", 32 | "babel-loader": "^8.1.0", 33 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 34 | "babel-preset-react-app": "^9.1.2", 35 | "css-loader": "^4.2.0", 36 | "enzyme": "^3.11.0", 37 | "enzyme-adapter-react-16": "^1.15.2", 38 | "jest": "^26.2.2", 39 | "sass-loader": "^9.0.2", 40 | "style-loader": "^1.2.1", 41 | "ts-jest": "^26.1.4", 42 | "typescript": "^3.9.7" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.statusCode = 200 5 | res.json({ name: 'John Doe' }) 6 | } 7 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const Home = () => { 4 | return

Welcome to My Next App!

; 5 | }; 6 | 7 | export default Home; 8 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trinwin/storybook-next-ts-template/363694179db1a2c61d3308e983e57df3338992af/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/global.scss: -------------------------------------------------------------------------------- 1 | html { 2 | background: #f1f1f1; 3 | max-width: 100%; 4 | } 5 | 6 | body { 7 | background: linear-gradient(315deg, var(#f1f1f1) 0%, var(#e7e7e7) 100%); 8 | font-family: "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", 9 | "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", 10 | "Helvetica Neue", system-ui, sans-serif !important; 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noImplicitAny": false, 16 | "jsx": "preserve", 17 | "baseUrl": "./", 18 | "paths": { 19 | "@components/*": ["./components/*"] 20 | } 21 | }, 22 | "exclude": ["node_modules"], 23 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 24 | } 25 | --------------------------------------------------------------------------------