├── .editorconfig ├── .eslintrc.json ├── .github └── pull_request_template.md ├── .gitignore ├── .husky └── commit-msg ├── .prettierrc ├── .storybook ├── main.js └── preview.js ├── README.md ├── babel.config.js ├── commitlint.config.js ├── jest.config.js ├── next.config.js ├── package.json ├── public ├── favicon.ico └── vercel.svg ├── src ├── __helpers__ │ ├── app-tests.tsx │ ├── image.tsx │ └── router.tsx ├── components │ ├── Button │ │ ├── Button.stories.tsx │ │ ├── Button.test.tsx │ │ ├── Button.tsx │ │ └── index.ts │ └── index.ts ├── pages │ ├── _app.tsx │ ├── _document.tsx │ └── index.tsx ├── setupTests.tsx └── styles │ ├── global.ts │ ├── index.ts │ └── theme.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true, 6 | "node": true 7 | }, 8 | "settings": { 9 | "react": { 10 | "version": "detect" 11 | } 12 | }, 13 | "extends": [ 14 | "eslint:recommended", 15 | "plugin:react/recommended", 16 | "plugin:@typescript-eslint/recommended", 17 | "plugin:@typescript-eslint/eslint-recommended", 18 | "plugin:prettier/recommended", 19 | "plugin:@next/next/recommended" 20 | ], 21 | "parser": "@typescript-eslint/parser", 22 | "parserOptions": { 23 | "ecmaFeatures": { 24 | "jsx": true 25 | }, 26 | "ecmaVersion": 2018, 27 | "sourceType": "module" 28 | }, 29 | "plugins": ["react", "react-hooks", "@typescript-eslint", "import"], 30 | "rules": { 31 | "@typescript-eslint/explicit-function-return-type": "off", 32 | "@typescript-eslint/explicit-member-accessibility": "off", 33 | "@typescript-eslint/explicit-module-boundary-types": "off", 34 | "@typescript-eslint/no-explicit-any": "off", 35 | "@typescript-eslint/no-non-null-assertion": "off", 36 | "@typescript-eslint/no-use-before-define": "off", 37 | "@typescript-eslint/no-var-requires": "off", 38 | "@typescript-eslint/camelcase": "off", 39 | "import/prefer-default-export": "off", 40 | "react-hooks/exhaustive-deps": "warn", 41 | "react-hooks/rules-of-hooks": "error", 42 | "react/jsx-one-expression-per-line": "off", 43 | "react/jsx-props-no-spreading": "off", 44 | "react/jsx-uses-react": "off", 45 | "react/prop-types": "off", 46 | "react/react-in-jsx-scope": "off", 47 | "react/require-default-props": "off", 48 | "import/first": "error" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Motivation 2 | 3 | Why is this change necessary? 4 | 5 | ## Proposed solution 6 | 7 | How does it address the issue? 8 | -------------------------------------------------------------------------------- /.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 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "singleQuote": true, 4 | 5 | "jsxSingleQuote": true, 6 | 7 | "semi": false, 8 | 9 | "trailingComma": "all" 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials", 9 | "@storybook/addon-interactions" 10 | ], 11 | "framework": "@storybook/react", 12 | "core": { 13 | "builder": "@storybook/builder-webpack5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | actions: { argTypesRegex: "^on[A-Z].*" }, 3 | controls: { 4 | matchers: { 5 | color: /(background|color)$/i, 6 | date: /Date$/, 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Hi there! 👋 2 | 3 | > This project was developed with care and in the best way I could with the knowledge I have, I hope you enjoy it! 🥳 4 | ## What is inside? 5 | 6 | - [Next](https://nextjs.org/docs) 7 | - [ReactJS](https://reactjs.org) 8 | - [TypeScript](https://www.typescriptlang.org) 9 | - [Styled Components](https://styled-components.com/docs) 10 | - [Storybook](https://storybook.js.org/docs/react/get-started/introduction) 11 | - [Jest](https://jestjs.io/docs/getting-started) 12 | - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) 13 | - [Eslint](https://eslint.org) 14 | - [Prettier](https://prettier.io) 15 | - [Husky](https://github.com/typicode/husky) 16 | - [Commitlint](https://commitlint.js.org/#/) 17 | 18 | ## Getting Started 19 | 20 | ### Install dependencies: 21 | 22 | ```bash 23 | yarn 24 | ``` 25 | 26 | or 27 | 28 | ```bash 29 | npm install 30 | ``` 31 | 32 | ### Run development server 33 | 34 | ```bash 35 | yarn dev 36 | ``` 37 | 38 | or 39 | 40 | ```bash 41 | npm run dev 42 | ``` 43 | 44 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 45 | 46 | ## Structure 47 | 48 | ``` 49 | └── src 50 | ├── __helpers__ 51 | ├── components 52 | ├── pages 53 | ├── styles 54 | ``` 55 | 56 | | Folder | Description | 57 | | ---------- | ------------------------------------------- | 58 | | **helpers** | Functions to handle the tests | 59 | | **components** | Page components | 60 | | **pages** | Pages components | 61 | | **styles** | Application styles | 62 | 63 | ## Conventions 64 | 65 | ### Components 66 | 67 | ``` 68 | └── 69 | ├── .tsx 70 | ├── .test.tsx 71 | ├── .styled.ts 72 | ├── .stories.tsx 73 | └── index.ts 74 | ``` 75 | 76 | | Files | Description | 77 | | --------------- | ---------------------------------------------- | 78 | | **.tsx** | Component implementation | 79 | | **.test.tsx** | Component tests | 80 | | **.styled.ts** | Component stylesheet using `styled-components` | 81 | | **.stories.tsx**| Storybook component | 82 | | **index.ts** | File to export the component | 83 | 84 | ## Commands 85 | 86 | - `dev`: run development server 87 | - `build`: creates the production build version 88 | - `lint`: runs the linter in all components and pages 89 | - `test`: runs jest to test all components and pages 90 | - `storybook`: runs docs with storybook 91 | 92 |

Made with 💜 by Giovanna

93 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['next/babel'], 3 | plugins: [['styled-components', { ssr: true }]], 4 | } 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | } -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const nextJest = require('next/jest') 2 | const { pathsToModuleNameMapper } = require('ts-jest') 3 | 4 | const { compilerOptions } = require('./tsconfig.json') 5 | 6 | const createJestConfig = nextJest({ 7 | dir: './', 8 | }) 9 | 10 | module.exports = createJestConfig({ 11 | testEnvironment: 'jsdom', 12 | testPathIgnorePatterns: ['/node_modules/'], 13 | setupFilesAfterEnv: ['/src/setupTests.tsx'], 14 | modulePaths: ['/src'], 15 | moduleDirectories: ['node_modules', '/src'], 16 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths), 17 | collectCoverage: true, 18 | collectCoverageFrom: ['src/**/*.ts(x)?'], 19 | }) 20 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-typescript-template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "prettier:check": "prettier -c", 11 | "lint:fix": "yarn lint --fix", 12 | "type-check": "tsc --project tsconfig.json --pretty --noEmit", 13 | "prepare": "husky install", 14 | "commit": "git-cz", 15 | "test": "jest --maxWorkers=50% --passWithNoTests", 16 | "test:watch": "jest --maxWorkers=50% --passWithNoTests --watch", 17 | "storybook": "start-storybook -p 6006", 18 | "build-storybook": "build-storybook" 19 | }, 20 | "dependencies": { 21 | "next": "12.2.5", 22 | "react": "18.2.0", 23 | "react-dom": "18.2.0", 24 | "styled-components": "5.3.5" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "7.18.13", 28 | "@commitlint/cli": "17.0.3", 29 | "@commitlint/config-conventional": "17.0.3", 30 | "@storybook/addon-actions": "6.5.10", 31 | "@storybook/addon-essentials": "6.5.10", 32 | "@storybook/addon-interactions": "6.5.10", 33 | "@storybook/addon-links": "6.5.10", 34 | "@storybook/builder-webpack5": "6.5.10", 35 | "@storybook/manager-webpack5": "6.5.10", 36 | "@storybook/react": "6.5.10", 37 | "@storybook/testing-library": "0.0.13", 38 | "@testing-library/jest-dom": "5.16.5", 39 | "@testing-library/react": "13.3.0", 40 | "@testing-library/react-hooks": "8.0.1", 41 | "@testing-library/user-event": "14.4.3", 42 | "@types/node": "18.7.11", 43 | "@types/react": "18.0.17", 44 | "@types/react-dom": "18.0.6", 45 | "@types/styled-components": "5.1.26", 46 | "@typescript-eslint/eslint-plugin": "5.34.0", 47 | "@typescript-eslint/parser": "5.34.0", 48 | "babel-loader": "8.2.5", 49 | "commitizen": "4.2.5", 50 | "cz-conventional-changelog": "3.3.0", 51 | "eslint": "8.22.0", 52 | "eslint-config-next": "12.2.5", 53 | "eslint-config-prettier": "8.5.0", 54 | "eslint-config-standard": "17.0.0", 55 | "eslint-plugin-import": "2.26.0", 56 | "eslint-plugin-node": "11.1.0", 57 | "eslint-plugin-prettier": "4.2.1", 58 | "eslint-plugin-promise": "6.0.0", 59 | "eslint-plugin-react": "7.30.1", 60 | "eslint-plugin-react-hooks": "4.6.0", 61 | "eslint-plugin-storybook": "0.6.4", 62 | "husky": "8.0.1", 63 | "jest": "28.1.3", 64 | "jest-environment-jsdom": "28.1.3", 65 | "prettier": "2.7.1", 66 | "ts-jest": "28.0.8", 67 | "typescript": "4.7.4" 68 | }, 69 | "eslintConfig": { 70 | "eslintConfig": { 71 | "extends": "./.eslintrc.json" 72 | } 73 | }, 74 | "config": { 75 | "commitizen": { 76 | "path": "./node_modules/cz-conventional-changelog" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giovannalinda/next-typescript-template/9a0d430340a13499842a8f6ab66040fba15f0347/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/__helpers__/app-tests.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, ReactNode } from 'react' 2 | import userEvent from '@testing-library/user-event' 3 | import { render as rtlRender } from '@testing-library/react' 4 | import { RouterContext } from 'next/dist/shared/lib/router-context' 5 | import { NextRouter } from 'next/router' 6 | 7 | import { createRouter } from './router' 8 | 9 | import './image' 10 | 11 | type RenderOptions = { 12 | router: Partial 13 | } 14 | 15 | type Props = { 16 | children: ReactNode 17 | } & RenderOptions 18 | 19 | function ReactWrapper({ children, router }: Props) { 20 | return ( 21 | 22 | {children} 23 | 24 | ) 25 | } 26 | 27 | function render(ui: ReactElement, { router }: RenderOptions = { router: {} }) { 28 | return rtlRender(ui, { 29 | wrapper: ({ children }) => ( 30 | {children} 31 | ), 32 | }) 33 | } 34 | 35 | export * from '@testing-library/react' 36 | export { userEvent, render } 37 | -------------------------------------------------------------------------------- /src/__helpers__/image.tsx: -------------------------------------------------------------------------------- 1 | import { ImageProps } from 'next/image' 2 | 3 | jest.mock('next/image', () => ({ 4 | __esModule: true, 5 | default: (props: ImageProps) => { 6 | return ( 7 | // eslint-disable-next-line @next/next/no-img-element 8 | {props.alt} 14 | ) 15 | }, 16 | })) 17 | -------------------------------------------------------------------------------- /src/__helpers__/router.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | import { LinkProps as NextLinkProps } from 'next/link' 3 | import { NextRouter } from 'next/router' 4 | 5 | type LinkProps = { 6 | children: ReactNode 7 | } & NextLinkProps 8 | 9 | jest.mock('next/link', () => { 10 | return function Link({ children, href, ...props }: LinkProps) { 11 | return ( 12 | 13 | {children} 14 | 15 | ) 16 | } 17 | }) 18 | 19 | export function createRouter(router: Partial): NextRouter { 20 | return { 21 | basePath: '', 22 | pathname: '/', 23 | route: '/', 24 | query: {}, 25 | locale: 'en-US', 26 | asPath: '/', 27 | back: jest.fn(), 28 | beforePopState: jest.fn(), 29 | prefetch: jest.fn(), 30 | push: jest.fn(), 31 | reload: jest.fn(), 32 | replace: jest.fn(), 33 | events: { 34 | on: jest.fn(), 35 | off: jest.fn(), 36 | emit: jest.fn(), 37 | }, 38 | isFallback: false, 39 | isLocaleDomain: false, 40 | isReady: true, 41 | defaultLocale: 'en-US', 42 | domainLocales: [], 43 | isPreview: false, 44 | ...router, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Button/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, Story } from '@storybook/react' 2 | import { Button } from './Button' 3 | 4 | export default { 5 | title: 'Button', 6 | component: Button, 7 | } as Meta 8 | 9 | export const Default: Story = () => 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button' 2 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button' 2 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { AppProps } from 'next/app' 2 | import Head from 'next/head' 3 | import { ThemeProvider } from 'styled-components' 4 | 5 | import { GlobalStyles, theme } from '~/styles' 6 | 7 | export default function App({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 11 | React Avançado 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { 2 | DocumentContext, 3 | Head, 4 | Html, 5 | Main, 6 | NextScript, 7 | } from 'next/document' 8 | import { ServerStyleSheet } from 'styled-components' 9 | 10 | export default class MyDocument extends Document { 11 | static async getInitialProps(ctx: DocumentContext) { 12 | const sheet = new ServerStyleSheet() 13 | const originalRenderPage = ctx.renderPage 14 | 15 | try { 16 | ctx.renderPage = () => 17 | originalRenderPage({ 18 | enhanceApp: (App) => (props) => 19 | sheet.collectStyles(), 20 | }) 21 | 22 | const initialProps = await Document.getInitialProps(ctx) 23 | return { 24 | ...initialProps, 25 | styles: [ 26 | <> 27 | {initialProps.styles} 28 | {sheet.getStyleElement()} 29 | , 30 | ], 31 | } 32 | } finally { 33 | sheet.seal() 34 | } 35 | } 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { NextPage } from 'next' 2 | import Head from 'next/head' 3 | 4 | import styled from 'styled-components' 5 | 6 | export const Main = styled.main` 7 | width: 100vw; 8 | height: 100vh; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | flex-direction: column; 13 | text-align: center; 14 | ` 15 | 16 | const Home: NextPage = () => { 17 | return ( 18 |
19 | 20 | Homepage 21 | 22 | 23 | 24 |
25 |

Hello World 🥳

26 | 27 |

28 | Get started by editing pages/index.tsx 29 |

30 |
31 |
32 | ) 33 | } 34 | 35 | export default Home 36 | -------------------------------------------------------------------------------- /src/setupTests.tsx: -------------------------------------------------------------------------------- 1 | import * as NextImage from 'next/image' 2 | import '@testing-library/jest-dom/extend-expect' 3 | 4 | const OriginalNextImage = NextImage.default 5 | 6 | Object.defineProperty(NextImage, 'default', { 7 | configurable: true, 8 | value: (props: NextImage.ImageProps) => { 9 | return 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /src/styles/global.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components' 2 | import { theme } from './theme' 3 | 4 | export const GlobalStyles = createGlobalStyle` 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | background: ${theme.colors.black}; 13 | font-family: ${theme.font.family}; 14 | overflow-x: hidden; 15 | color: ${theme.colors.white}; 16 | } 17 | 18 | button { 19 | cursor: pointer; 20 | border: none; 21 | } 22 | 23 | a { 24 | text-decoration: none; 25 | } 26 | 27 | ul { 28 | list-style: none; 29 | } 30 | 31 | &::selection { 32 | color: ${theme.colors.black}; 33 | background: ${theme.colors.white}; 34 | } 35 | ` 36 | -------------------------------------------------------------------------------- /src/styles/index.ts: -------------------------------------------------------------------------------- 1 | export { GlobalStyles } from './global' 2 | export { theme } from './theme' 3 | -------------------------------------------------------------------------------- /src/styles/theme.ts: -------------------------------------------------------------------------------- 1 | export const theme = { 2 | font: { 3 | family: 'Inter', 4 | weigths: { 5 | regular: 400, 6 | medium: 500, 7 | semiBold: 600, 8 | bold: 700, 9 | }, 10 | }, 11 | colors: { 12 | black: '#000000', 13 | white: '#FFFFFF', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /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 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": "./src", 18 | "paths": { 19 | "~/*": ["*"] 20 | } 21 | }, 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 23 | "exclude": ["node_modules"] 24 | } 25 | --------------------------------------------------------------------------------