├── jest.config.js
├── src
├── index.tsx
├── hooks
│ ├── useCounter.spec.tsx
│ └── useCounter.tsx
├── stories
│ └── Button.stories.mdx
└── components
│ ├── Button.spec.tsx
│ └── Button.tsx
├── babel.config.js
├── .gitignore
├── tsconfig.json
├── .storybook
└── main.js
├── LICENSE
├── .eslintrc.js
├── README.md
└── package.json
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'jsdom'
4 | };
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | export { Button } from './components/Button';
2 | export { useCounter } from './hooks/useCounter';
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | '@babel/preset-typescript'
11 | ],
12 | ],
13 | };
--------------------------------------------------------------------------------
/src/hooks/useCounter.spec.tsx:
--------------------------------------------------------------------------------
1 | import { renderHook, act } from '@testing-library/react-hooks'
2 | import { useCounter } from './useCounter'
3 |
4 | test('should increment counter', () => {
5 | const { result } = renderHook(() => useCounter())
6 |
7 | act(() => {
8 | result.current.increment()
9 | })
10 |
11 | expect(result.current.count).toBe(1)
12 | })
--------------------------------------------------------------------------------
/src/hooks/useCounter.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react'
2 |
3 | interface UseCounterProps {
4 | count: number;
5 | increment: () => void;
6 | }
7 |
8 | export const useCounter = (): UseCounterProps => {
9 | const [count, setCount] = useState(0)
10 |
11 | const increment = useCallback(() => setCount((x) => x + 1), [])
12 |
13 | return { count, increment }
14 | }
15 |
--------------------------------------------------------------------------------
/src/stories/Button.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Story, Preview, Props } from "@storybook/addon-docs/blocks";
2 | import { Button } from "../components/Button";
3 |
4 |
5 |
6 |
7 | The world's most boring button!
8 |
9 | # Preview
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/Button.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import '@testing-library/jest-dom/extend-expect';
3 | import {render, screen} from '@testing-library/react'
4 | import { Button } from './Button'
5 |
6 | test('shows the children of the button', () => {
7 | const testMessage = 'Test Message'
8 | render()
9 | expect(screen.getByText(testMessage)).toBeInTheDocument()
10 | })
--------------------------------------------------------------------------------
/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import React, { FunctionComponent } from "react";
2 |
3 | interface ButtonProps {
4 | /**
5 | * Simple click handler
6 | */
7 | onClick?: () => void;
8 | }
9 |
10 | /**
11 | * The world's most _basic_ button
12 | */
13 | export const Button: FunctionComponent = ({ children, onClick }) => (
14 |
17 | );
--------------------------------------------------------------------------------
/.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 | # production
12 | /build
13 | /storybook-static
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "build",
4 | "declaration": true,
5 | "target": "es5",
6 | "lib": [
7 | "dom",
8 | "dom.iterable",
9 | "esnext"
10 | ],
11 | "allowJs": true,
12 | "skipLibCheck": true,
13 | "esModuleInterop": true,
14 | "allowSyntheticDefaultImports": true,
15 | "strict": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "module": "esnext",
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "jsx": "react"
22 | },
23 | "include": [
24 | "src"
25 | ],
26 | "exclude": [
27 | "src/**/*.spec.tsx",
28 | "src/setupTests.tsx",
29 | "src/stories",
30 | "src/*.ts"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 |
4 | module.exports = {
5 | stories: ['../src/**/*.stories.mdx'],
6 | addons: [
7 | '@storybook/addon-actions',
8 | '@storybook/addon-links',
9 | '@storybook/addon-docs',
10 | {
11 | name: '@storybook/preset-typescript',
12 | options: {
13 | tsLoaderOptions: {
14 | configFile: path.resolve(__dirname, '../tsconfig.json'),
15 | },
16 | tsDocgenLoaderOptions: {
17 | tsconfigPath: path.resolve(__dirname, '../tsconfig.json'),
18 | },
19 | forkTsCheckerWebpackPluginOptions: {
20 | colors: false, // disables built-in colors in logger messages
21 | },
22 | include: [path.resolve(__dirname, '../src')],
23 | },
24 | },
25 | ],
26 | };
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 | -----------
3 |
4 | Copyright (c) 2020
5 | Permission is hereby granted, free of charge, to any person
6 | obtaining a copy of this software and associated documentation
7 | files (the "Software"), to deal in the Software without
8 | restriction, including without limitation the rights to use,
9 | copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following
12 | conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:react/recommended",
10 | "plugin:jest/recommended",
11 | "plugin:jest-dom/recommended",
12 | "plugin:@typescript-eslint/eslint-recommended",
13 | "plugin:@typescript-eslint/recommended"
14 | ],
15 | "globals": {
16 | "Atomics": "readonly",
17 | "SharedArrayBuffer": "readonly"
18 | },
19 | "parser": "@typescript-eslint/parser",
20 | "parserOptions": {
21 | "ecmaFeatures": {
22 | "jsx": true
23 | },
24 | "ecmaVersion": 2018,
25 | "sourceType": "module"
26 | },
27 | "plugins": [
28 | "react",
29 | "jest",
30 | "jest-dom",
31 | "react-hooks",
32 | "@typescript-eslint"
33 | ],
34 | "rules": {
35 | "react-hooks/rules-of-hooks": "error",
36 | "react-hooks/exhaustive-deps": "warn",
37 | "react/prop-types": 0
38 | },
39 | "settings": {
40 | "react": {
41 | "version": "detect"
42 | }
43 | }
44 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecated. Just use [TSDX](https://github.com/jaredpalmer/tsdx) from @jaredpalmer instead.
2 |
3 |
4 | ## react-library-template
5 | ### (formerly Storybook TypeScript React Component Library)
6 |
7 | This repo attempts to solve the problem of how to share react hooks, components, and utilities with your team (or all of your personal projects). There are other solutions out there, such as create-react-library, but they are no longer updated and out of date.
8 |
9 | By utilitizing Storybook, we are able to bypass any need for webpack and create-react-app. All component development can be done in isolation and can be published to a github page for other stakeholders to see.
10 |
11 | This library uses [TypeScript](https://www.typescriptlang.org/), [react-testing-library](https://github.com/testing-library/react-testing-library), [Jest](https://jestjs.io/), and [ESLint](https://eslint.org/) and has everything preconfigured so you can skip straight to developing.
12 |
13 | ## Setup
14 |
15 | * Clone this template repo by clicking "Use this template"
16 | * Rename it to your liking (e.g., my-company-react-library) and clone it locally
17 | * Rename the `package.json` name field, add any dependencies you need, and remove any parts of this README you no longer need
18 | * Run this project using the scripts below!
19 |
20 | ## Developing with another library
21 |
22 | Sometimes it is helpful be able to use the latest code as you develop in another project that you intend to consume this library with.
23 |
24 | In order to do so:
25 |
26 | * Run `npm run build:watch` in this project
27 | * In your other project, run `npm link ../react-library-template` replacing the folder name with your new repo's name
28 | * Start your other project and you will have access to the compiled code as you write it!
29 |
30 | ## Available scripts
31 |
32 | ### `start`
33 |
34 | Starts the storybook site at http://localhost:6006/
35 |
36 | ### `build`
37 | **Executes the following sub scripts:**
38 | #### `build:lint`
39 | Lints using eslint (using plugins from jest, jest-dom, react, and react-hooks)
40 | #### `build:library`
41 | Builds your library and copies all css / woff2 / svgs / png files to the build folder
42 | #### `build:storybook`
43 | Creates a static storybook site that you can host
44 |
45 | ### `test`
46 |
47 | Runs all tests with Jest and react-testing-library in watch mode
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-library-template",
3 | "version": "0.1.0",
4 | "main": "build/index.js",
5 | "files": [
6 | "build"
7 | ],
8 | "scripts": {
9 | "start": "npm run storybook",
10 | "build": "npm run lint && npm run build:library && npm run build:storybook",
11 | "build:library": "rimraf build && 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",
12 | "build:storybook": "build-storybook -s public",
13 | "build:watch": "rimraf build && tsc --watch",
14 | "test": "jest --watch",
15 | "lint": "eslint 'src/**/*.{ts,tsx}'",
16 | "storybook": "start-storybook -p 6006",
17 | "copy-css-to-lib": "cpx \"./src/**/*.css\" ./build",
18 | "copy-woff2-to-lib": "cpx \"./src/**/*.woff2\" ./build",
19 | "copy-svg-to-lib": "cpx \"./src/**/*.svg\" ./build",
20 | "copy-png-to-lib": "cpx \"./src/**/*.png\" ./build",
21 | "build-storybook": "build-storybook"
22 | },
23 | "peerDependencies": {
24 | "react": "^16.12.0",
25 | "react-dom": "^16.12.0"
26 | },
27 | "devDependencies": {
28 | "@babel/core": "^7.8.3",
29 | "@babel/preset-env": "^7.8.3",
30 | "@babel/preset-typescript": "^7.8.3",
31 | "@storybook/addon-actions": "^5.3.3",
32 | "@storybook/addon-docs": "^5.3.3",
33 | "@storybook/addon-links": "^5.3.3",
34 | "@storybook/addons": "^5.3.3",
35 | "@storybook/preset-typescript": "^1.2.0",
36 | "@storybook/react": "^5.3.3",
37 | "@testing-library/jest-dom": "^4.2.4",
38 | "@testing-library/react": "^9.3.2",
39 | "@testing-library/react-hooks": "^3.2.1",
40 | "@testing-library/user-event": "^7.1.2",
41 | "@types/jest": "^24.0.25",
42 | "@types/node": "^12.0.0",
43 | "@types/react": "^16.9.0",
44 | "@types/react-dom": "^16.9.0",
45 | "@typescript-eslint/eslint-plugin": "^2.16.0",
46 | "@typescript-eslint/parser": "^2.16.0",
47 | "babel-jest": "^24.9.0",
48 | "babel-loader": "^8.0.6",
49 | "cpx": "^1.5.0",
50 | "eslint": "^6.8.0",
51 | "eslint-plugin-jest": "^23.6.0",
52 | "eslint-plugin-jest-dom": "^2.0.0",
53 | "eslint-plugin-react": "^7.17.0",
54 | "eslint-plugin-react-hooks": "^2.3.0",
55 | "fork-ts-checker-webpack-plugin": "^3.1.1",
56 | "jest": "^24.9.0",
57 | "react-docgen-typescript-loader": "^3.6.0",
58 | "react-test-renderer": "^16.12.0",
59 | "rimraf": "^3.0.0",
60 | "ts-jest": "^24.3.0",
61 | "ts-loader": "^6.2.1",
62 | "typescript": "^3.7.4"
63 | },
64 | "dependencies": {}
65 | }
66 |
--------------------------------------------------------------------------------