├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── @types ├── mdx-js__mdx │ ├── index.d.ts │ ├── mdx-hast-to-jsx.d.ts │ └── package.json ├── mdx-js__react │ ├── index.d.ts │ └── package.json ├── remark-autolink-headings │ ├── index.d.ts │ └── package.json ├── remark-mdx │ ├── index.d.ts │ └── package.json └── remark-slug │ ├── index.d.ts │ └── package.json ├── LICENSE ├── README.md ├── package.json ├── packages ├── backoff │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── calculate-delay.test.ts │ │ ├── calculate-delay.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── interleave.test.ts │ │ ├── interleave.ts │ │ ├── timeout.test.ts │ │ ├── timeout.ts │ │ └── types.ts │ └── tsconfig.json ├── example-packages │ ├── .gitignore │ ├── cli.js │ ├── index.js │ ├── nodemon.build.json │ ├── nodemon.example-packages.json │ ├── package.json │ ├── src │ │ ├── cli.ts │ │ ├── main.ts │ │ ├── read-files-recursively.ts │ │ └── sub-directories.ts │ └── tsconfig.json ├── examples │ ├── jest.config.js │ ├── package.json │ └── src │ │ ├── best-business-logic │ │ ├── .eslintrc │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── best-context │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── best-performance │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── best-prefer-primitive │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── best-props │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── class-bitcoin │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── example.test.tsx.snap │ │ │ └── fc-example.test.tsx.snap │ │ │ ├── bitcoin-api.ts │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ ├── fc-example.test.tsx │ │ │ ├── fc-example.tsx │ │ │ └── testing │ │ │ ├── deferred.ts │ │ │ └── index.ts │ │ ├── class-simple │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── components-stateful │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── components-stateless │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── context-complex │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── button.default.module.css │ │ │ ├── button.green.module.css │ │ │ ├── button.red.module.css │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── context-simple │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── custom-hooks-click-out │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── custom-hooks-formik │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── custom-hooks-navigation │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── accordion-example.test.tsx.snap │ │ │ └── example.test.tsx.snap │ │ │ ├── accordion-example.test.tsx │ │ │ ├── accordion-example.tsx │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ ├── navigation.test.tsx │ │ │ └── navigation.tsx │ │ ├── forms-events │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── forms-formik │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ └── user-api.ts │ │ ├── forms-simple-form │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ └── user-api.ts │ │ ├── forms-simple-input │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hoc-theme │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ └── theme-library │ │ │ ├── __snapshots__ │ │ │ ├── themes.test.tsx.snap │ │ │ └── with-theme.test.tsx.snap │ │ │ ├── index.ts │ │ │ ├── internal │ │ │ └── theme-context.tsx │ │ │ ├── themes.test.tsx │ │ │ ├── themes.tsx │ │ │ ├── with-theme.test.tsx │ │ │ └── with-theme.tsx │ │ ├── hooks-use-effect │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-imperative-handle │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-memo │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-reducer │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-ref-alt │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-ref │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── hooks-use-state │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── intro-dynamic-clock │ │ ├── .extends │ │ └── src │ │ │ ├── index.tsx │ │ │ └── render.tsx │ │ ├── intro-hello-world │ │ ├── .extends │ │ └── src │ │ │ └── render.tsx │ │ ├── intro-static-clock │ │ ├── .extends │ │ └── src │ │ │ ├── index.tsx │ │ │ └── render.tsx │ │ ├── jsx-interpolate │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── jsx-prop-types │ │ ├── .eslintrc │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── jsx-props-spread │ │ ├── .eslintrc │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── jsx-props │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── jsx-repeat │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── jsx-simple │ │ ├── .extends │ │ └── src │ │ │ └── example.tsx │ │ ├── routing-fallback │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── routing-params │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── database.ts │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── routing-simple │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ └── example.tsx │ │ ├── styling-global │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── example.test.tsx.snap │ │ │ └── slider.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ ├── global.css │ │ │ ├── slider.test.tsx │ │ │ └── slider.tsx │ │ ├── styling-module │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── example.test.tsx.snap │ │ │ └── slider.test.tsx.snap │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ ├── slider.test.tsx │ │ │ └── slider.tsx │ │ ├── styling-styled │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── example.test.tsx.snap │ │ │ └── slider.test.tsx.snap │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ ├── slider.test.tsx │ │ │ └── slider.tsx │ │ ├── testing-components │ │ ├── .eslintrc │ │ ├── .extends │ │ ├── .test │ │ └── src │ │ │ ├── __snapshots__ │ │ │ └── example.test.tsx.snap │ │ │ ├── example.module.css │ │ │ ├── example.test.tsx │ │ │ ├── example.tsx │ │ │ └── tenor-api.ts │ │ └── testing-dummy │ │ ├── .extends │ │ └── src │ │ └── index.test.ts ├── gatsby-plugin-codesandbox │ ├── gatsby-node.js │ ├── index.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── gatsby-node.ts │ │ ├── index.ts │ │ ├── sort-files.ts │ │ ├── types.ts │ │ └── upload.ts │ └── tsconfig.json ├── gatsby-remark-lift │ ├── index.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── gatsby-transformer-file-content │ ├── gatsby-node.js │ ├── index.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ └── gatsby-node.ts │ └── tsconfig.json ├── gatsby │ ├── gatsby-browser.js │ ├── gatsby-config.js │ ├── gatsby │ │ ├── assets │ │ │ ├── dracula-theme.theme-dracula-2.22.1.vsix │ │ │ └── jpoissonnier.vscode-styled-components-1.2.0.vsix │ │ ├── gatsby-config.ts │ │ ├── gatsby-remark-plugins.ts │ │ ├── rehype-plugins.ts │ │ ├── types.ts │ │ └── typography.ts │ ├── nodemon.json │ ├── package.json │ ├── plugins │ │ └── example-packages-to-codesandbox │ │ │ ├── gatsby-node.js │ │ │ ├── package.json │ │ │ └── src │ │ │ └── gatsby-node.ts │ └── src │ │ ├── assets │ │ └── styles.css │ │ ├── components │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── slug.ts │ │ ├── icons │ │ │ ├── arrow.tsx │ │ │ ├── beaker.tsx │ │ │ ├── code-sandbox.tsx │ │ │ ├── icon.tsx │ │ │ ├── index.ts │ │ │ ├── menu.tsx │ │ │ └── reset.tsx │ │ ├── index.ts │ │ ├── mdx │ │ │ ├── default-mdx-provider.tsx │ │ │ ├── example.tsx │ │ │ └── index.ts │ │ ├── navigation │ │ │ ├── footer.tsx │ │ │ ├── index.ts │ │ │ ├── link-to-slug.tsx │ │ │ ├── navigation.tsx │ │ │ └── prev-next.tsx │ │ ├── theme │ │ │ ├── default-theme-provider.tsx │ │ │ ├── index.ts │ │ │ └── theme.ts │ │ └── util │ │ │ ├── index.ts │ │ │ └── seo.tsx │ │ ├── content │ │ ├── about-jsx.mdx │ │ ├── advanced-hooks.mdx │ │ ├── best-practices.mdx │ │ ├── class-components.mdx │ │ ├── context.mdx │ │ ├── custom-hooks.mdx │ │ ├── events-forms.mdx │ │ ├── final-words.mdx │ │ ├── function-components.mdx │ │ ├── getting-started.mdx │ │ ├── higher-order-components.mdx │ │ ├── hooks.mdx │ │ ├── index.tsx │ │ ├── navigation.yaml │ │ ├── routing.mdx │ │ ├── styling.mdx │ │ └── testing.mdx │ │ ├── graphql │ │ ├── index.ts │ │ ├── mdx-title.ts │ │ ├── navigation.ts │ │ └── site-metadata.ts │ │ └── layouts │ │ └── default.tsx ├── inherit-files │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── register-typescript │ ├── index.d.ts │ ├── index.js │ └── package.json ├── rehype-mdx-internal-links │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── rehype-mdx-wrap-with-component │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── get-comment-value.ts │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── remark-inject │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── create.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── inject.ts │ │ └── types.ts │ └── tsconfig.json ├── semaphore │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── templates │ ├── .gitignore │ ├── package.json │ └── src │ │ ├── react │ │ ├── .extends │ │ └── src │ │ │ ├── example.tsx │ │ │ └── index.tsx │ │ ├── simple-react │ │ ├── .editorconfig │ │ ├── .prettierrc │ │ ├── package.json │ │ ├── public │ │ │ └── index.html │ │ ├── src │ │ │ ├── index.tsx │ │ │ ├── react-app-env.d.ts │ │ │ └── setupTests.ts │ │ └── tsconfig.json │ │ ├── with-css-module │ │ └── src │ │ │ └── css-module.d.ts │ │ ├── with-formik │ │ └── package.json │ │ ├── with-router │ │ ├── package.json │ │ └── src │ │ │ ├── example.tsx │ │ │ ├── index.tsx │ │ │ ├── mock-browser-router.tsx │ │ │ └── render.tsx │ │ ├── with-styled-components │ │ ├── package.json │ │ └── src │ │ │ └── setupTests.ts │ │ └── with-testing │ │ └── sandbox.config.json ├── unified-filter │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── unified-parse-yaml │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json └── unist-util-visit-async │ ├── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── src │ ├── __snapshots__ │ │ └── index.test.ts.snap │ ├── index.test.ts │ └── index.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── pnpmfile.js ├── tsconfig.dist.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | indent_size = 2 4 | indent_style = space 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | public 3 | dist 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: push 3 | jobs: 4 | build: 5 | runs-on: ubuntu-20.04 6 | steps: 7 | - uses: actions/checkout@v2.3.4 8 | - uses: actions/setup-node@v1.4.4 9 | with: 10 | node-version: '14' 11 | - run: npm i -g pnpm@5 12 | - run: pnpm i 13 | - run: pnpm run build-requirements 14 | - run: pnpm test 15 | - run: pnpm run gatsby -- build 16 | - uses: actions/upload-artifact@v2.2.1 17 | with: 18 | name: gatsby-site 19 | path: packages/gatsby/public 20 | 21 | deploy: 22 | runs-on: ubuntu-20.04 23 | needs: build 24 | if: github.ref == 'refs/heads/master' 25 | steps: 26 | - uses: actions/download-artifact@v2 27 | with: 28 | name: gatsby-site 29 | path: gatsby-site 30 | - uses: peaceiris/actions-gh-pages@v3.7.3 31 | with: 32 | deploy_key: ${{ secrets.REACTBYEXAMPLE_GITHUB_IO_DEPLOY_KEY }} 33 | external_repository: reactbyexample/reactbyexample.github.io 34 | publish_branch: master 35 | publish_dir: gatsby-site 36 | user_name: github-actions[bot] 37 | user_email: github-actions[bot]@users.noreply.github.com 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variable files 55 | .env* 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | dist 72 | 73 | .vscode/ 74 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | public 3 | dist 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "trailingComma": "all", 4 | "semi": false, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /@types/mdx-js__mdx/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const mdx: (mdx: string) => string 2 | export default mdx 3 | -------------------------------------------------------------------------------- /@types/mdx-js__mdx/mdx-hast-to-jsx.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'unified' 2 | import { Node } from 'unist' 3 | 4 | declare const mdxHastToJsx: Plugin 5 | export default mdxHastToJsx 6 | 7 | export declare const toJSX: (node: Node, parentNode?: Node) => string 8 | -------------------------------------------------------------------------------- /@types/mdx-js__mdx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/mdx-js__mdx", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "", 6 | "types": "index.d.ts", 7 | "dependencies": { 8 | "@types/unist": "2.0.3", 9 | "unified": "9.2.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /@types/mdx-js__react/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType, FC } from 'react' 2 | 3 | type Tag = 4 | | 'a' 5 | | 'blockquote' 6 | | 'code' 7 | | 'delete' 8 | | 'em' 9 | | 'h1' 10 | | 'h2' 11 | | 'h3' 12 | | 'h4' 13 | | 'h5' 14 | | 'h6' 15 | | 'hr' 16 | | 'img' 17 | | 'inlineCode' 18 | | 'li' 19 | | 'ol' 20 | | 'p' 21 | | 'pre' 22 | | 'strong' 23 | | 'sup' 24 | | 'table' 25 | | 'td' 26 | | 'thematicBreak' 27 | | 'tr' 28 | | 'ul' 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 31 | export type Components = Partial>> 32 | 33 | export interface MDXProviderProps { 34 | components?: Components 35 | } 36 | 37 | export const MDXProvider: FC 38 | -------------------------------------------------------------------------------- /@types/mdx-js__react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/mdx-js__react", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "", 6 | "types": "index.d.ts", 7 | "dependencies": { 8 | "@types/react": "16.9.49" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /@types/remark-autolink-headings/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'unified' 2 | import { Node } from 'unist' 3 | 4 | export interface Options { 5 | behavior?: 'prepend' | 'append' | 'wrap' | 'before' | 'after' 6 | linkProperties?: Record 7 | content?: Node | Node[] | ((node: Node) => Node | Node[]) 8 | group?: (node: Node) => Node 9 | } 10 | declare const remarkSlug: Plugin<[Options]> 11 | export default remarkSlug 12 | -------------------------------------------------------------------------------- /@types/remark-autolink-headings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/remark-autolink-headings", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "", 6 | "types": "index.d.ts", 7 | "dependencies": { 8 | "@types/unist": "2.0.3", 9 | "unified": "9.2.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /@types/remark-mdx/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'unified' 2 | 3 | declare const remarkMdx: Plugin 4 | export default remarkMdx 5 | -------------------------------------------------------------------------------- /@types/remark-mdx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/remark-mdx", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "", 6 | "types": "index.d.ts", 7 | "dependencies": { 8 | "unified": "9.2.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /@types/remark-slug/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'unified' 2 | 3 | declare const remarkSlug: Plugin 4 | export default remarkSlug 5 | -------------------------------------------------------------------------------- /@types/remark-slug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/remark-slug", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "", 6 | "types": "index.d.ts", 7 | "dependencies": { 8 | "unified": "9.2.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Barnabas Forgo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React by Example 2 | 3 | ## Requirements 4 | 5 | - [Node 12](https://nodejs.org/) 6 | - [pnpm 5](https://pnpm.js.org/) 7 | 8 | ## Running 9 | 10 | ```sh 11 | # setup 12 | pnpm i 13 | 14 | # watch everything 15 | # ignore errors, wait until ready message 16 | # laptop might take off 17 | pnpm start 18 | 19 | # or just the gatsby site 20 | pnpm run build-requirements && pnpm run start-gatsby 21 | ``` 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-by-example", 3 | "version": "1.0.0", 4 | "engines": { 5 | "node": ">=12", 6 | "pnpm": ">=5" 7 | }, 8 | "scripts": { 9 | "start": "pnpm --parallel start", 10 | "test": "pnpm --parallel test", 11 | "build": "pnpm -r build", 12 | "pre-gatsby": "pnpm --filter \"@app/gatsby^...\"", 13 | "gatsby": "pnpm --filter \"@app/gatsby\"", 14 | "build-requirements": "pnpm run pre-gatsby -- run build", 15 | "start-gatsby": "pnpm run gatsby -- run start" 16 | }, 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@types/eslint": "7.2.1", 20 | "@typescript-eslint/eslint-plugin": "3.10.1", 21 | "@typescript-eslint/parser": "3.10.1", 22 | "eslint": "7.8.1", 23 | "eslint-config-airbnb": "18.2.0", 24 | "eslint-config-airbnb-typescript": "9.0.0", 25 | "eslint-config-prettier": "6.11.0", 26 | "eslint-plugin-import": "2.22.0", 27 | "eslint-plugin-jsx-a11y": "6.3.1", 28 | "eslint-plugin-react-hooks": "4.1.0", 29 | "eslint-plugin-react": "7.20.6", 30 | "prettier": "2.1.1", 31 | "typescript": "4.0.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/backoff/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist') 2 | -------------------------------------------------------------------------------- /packages/backoff/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | } 4 | -------------------------------------------------------------------------------- /packages/backoff/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts" 4 | } 5 | -------------------------------------------------------------------------------- /packages/backoff/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@app/backoff", 3 | "version": "1.0.0", 4 | "private": true, 5 | "types": "dist", 6 | "scripts": { 7 | "start": "nodemon -x \"run-s build\"", 8 | "build": "run-s build:*", 9 | "build:clean": "rimraf dist", 10 | "build:tsc": "tsc -p ./tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch" 13 | }, 14 | "devDependencies": { 15 | "@types/jest": "26.0.13", 16 | "jest": "26.4.2", 17 | "nodemon": "2.0.4", 18 | "npm-run-all": "4.1.5", 19 | "rimraf": "3.0.2", 20 | "ts-jest": "26.3.0", 21 | "typescript": "4.0.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/backoff/src/calculate-delay.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/tim-kos/node-retry/tree/b316bfc196a89504fa348bcb97b20eb55509a295#retrytimeoutsoptions 2 | export const calculateDelay = ( 3 | attempt: number, 4 | minTimeout: number, 5 | maxTimeout: number, 6 | factor: number, 7 | randomize: boolean, 8 | ): number => { 9 | const random = randomize ? Math.random() + 1 : 1 10 | 11 | return Math.min( 12 | Math.round(random * minTimeout * factor ** attempt), 13 | maxTimeout, 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/backoff/src/interleave.test.ts: -------------------------------------------------------------------------------- 1 | import { interleave } from './interleave' 2 | 3 | function* integers(start = 0) { 4 | let i = start 5 | while (true) { 6 | yield i 7 | i += 1 8 | } 9 | } 10 | 11 | function* fib() { 12 | yield 0 13 | yield 1 14 | let prev = [0, 1] 15 | while (true) { 16 | const [l, r] = prev 17 | const next = l + r 18 | prev = [r, next] 19 | yield next 20 | } 21 | } 22 | 23 | function* take(iterable: Iterable, n: number) { 24 | let count = 0 25 | for (const next of iterable) { 26 | if (count >= n) return 27 | yield next 28 | count += 1 29 | } 30 | if (count < n) throw new Error() 31 | } 32 | 33 | describe('interleave', () => { 34 | it.each([ 35 | [ 36 | ['a', 'b', 'c'], 37 | [0, 1], 38 | ['a', 0, 'b', 1, 'c'], 39 | ], 40 | [['a', 'b', 'c'], integers(), ['a', 0, 'b', 1, 'c']], 41 | ])('should interleave', (array, iterable, expected) => { 42 | expect(Array.from(interleave(array, iterable))).toEqual(expected) 43 | }) 44 | 45 | it('should take infinite iterables', () => { 46 | expect(Array.from(take(interleave(integers(-99), fib()), 21))).toEqual([ 47 | -99, 48 | 0, 49 | -98, 50 | 1, 51 | -97, 52 | 1, 53 | -96, 54 | 2, 55 | -95, 56 | 3, 57 | -94, 58 | 5, 59 | -93, 60 | 8, 61 | -92, 62 | 13, 63 | -91, 64 | 21, 65 | -90, 66 | 34, 67 | -89, 68 | ]) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /packages/backoff/src/interleave.ts: -------------------------------------------------------------------------------- 1 | export const interleave = ( 2 | leftIterable: Iterable, 3 | rightIterable: Iterable, 4 | ): Iterable => { 5 | const rightIterator = rightIterable[Symbol.iterator]() 6 | 7 | return (function* interleaved() { 8 | let first = true 9 | for (const leftValue of leftIterable) { 10 | if (!first) { 11 | const rightResult = rightIterator.next() 12 | if (rightResult.done) throw new Error() 13 | yield rightResult.value 14 | } 15 | yield leftValue 16 | first = false 17 | } 18 | })() 19 | } 20 | -------------------------------------------------------------------------------- /packages/backoff/src/timeout.test.ts: -------------------------------------------------------------------------------- 1 | import { timeout } from './timeout' 2 | 3 | describe('timeout', () => { 4 | let resolved = false 5 | 6 | beforeEach(() => { 7 | jest.useFakeTimers() 8 | const usesTimeout = async () => { 9 | await timeout(500) 10 | resolved = true 11 | } 12 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 13 | usesTimeout() 14 | }) 15 | 16 | afterEach(() => { 17 | jest.useRealTimers() 18 | }) 19 | 20 | it('should not resolve yet', () => { 21 | expect(resolved).toBe(false) 22 | }) 23 | 24 | describe('when timeout is over', () => { 25 | beforeEach(() => { 26 | jest.advanceTimersByTime(500) 27 | }) 28 | 29 | it('should resolve', () => { 30 | expect(resolved).toBe(true) 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/backoff/src/timeout.ts: -------------------------------------------------------------------------------- 1 | export const timeout = (ms?: number): Promise => 2 | new Promise((resolve) => setTimeout(resolve, ms)) 3 | -------------------------------------------------------------------------------- /packages/backoff/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface BackoffArgs { 2 | maxAttempts?: number 3 | factor?: number 4 | minTimeout?: number 5 | maxTimeout?: number 6 | randomize?: boolean 7 | } 8 | -------------------------------------------------------------------------------- /packages/backoff/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.dist.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "outDir": "dist" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/example-packages/.gitignore: -------------------------------------------------------------------------------- 1 | example-packages/ 2 | -------------------------------------------------------------------------------- /packages/example-packages/cli.js: -------------------------------------------------------------------------------- 1 | require('./dist/cli') 2 | -------------------------------------------------------------------------------- /packages/example-packages/index.js: -------------------------------------------------------------------------------- 1 | // noop 2 | -------------------------------------------------------------------------------- /packages/example-packages/nodemon.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts" 4 | } 5 | -------------------------------------------------------------------------------- /packages/example-packages/nodemon.example-packages.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["node_modules/@app"], 3 | "ext": "*", 4 | "ignoreRoot": [], 5 | "ignore": [], 6 | "watchOptions": { 7 | "ignored": ["**/@app/*/node_modules/**"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/example-packages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@app/example-packages", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "run-s watch", 7 | "build": "run-s build:*", 8 | "build:clean": "rimraf dist", 9 | "build:tsc": "tsc -p ./tsconfig.json", 10 | "build:example-packages": "run-s build:example-packages:*", 11 | "build:example-packages:clean": "rimraf example-packages", 12 | "build:example-packages:cli": "node cli node_modules/@app/templates/src node_modules/@app/examples/src example-packages", 13 | "watch": "run-p watch:*", 14 | "watch:build": "nodemon --config nodemon.build.json -x \"run-s build\"", 15 | "watch:example-packages": "nodemon --config nodemon.example-packages.json -x \"run-s build:example-packages\"" 16 | }, 17 | "peerDependencies": { 18 | "@types/history": "4.6.2", 19 | "@types/react": "16.9.49", 20 | "@types/react-dom": "16.9.8", 21 | "@types/react-router": "5.1.8", 22 | "@types/styled-components": "5.1.3", 23 | "formik": "2.2.0", 24 | "history": "4.9.0", 25 | "react": "16.13.1", 26 | "react-dom": "16.13.1", 27 | "react-is": "16.13.1", 28 | "react-router": "5.2.0", 29 | "react-router-dom": "5.2.0", 30 | "styled-components": "5.2.0" 31 | }, 32 | "devDependencies": { 33 | "@app/examples": "workspace:*", 34 | "@app/inherit-files": "workspace:*", 35 | "@app/templates": "workspace:*", 36 | "@types/node": "14.6.4", 37 | "nodemon": "2.0.4", 38 | "npm-run-all": "4.1.5", 39 | "rimraf": "3.0.2", 40 | "typescript": "4.0.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/example-packages/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { main } from './main' 2 | 3 | const [ 4 | templatesDirectory, 5 | examplesDirectory, 6 | outputDirectory, 7 | ] = process.argv.slice(2) 8 | 9 | main(templatesDirectory, examplesDirectory, outputDirectory).catch((e) => { 10 | // eslint-disable-next-line no-console 11 | console.error(e) 12 | process.exit(1) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/example-packages/src/read-files-recursively.ts: -------------------------------------------------------------------------------- 1 | import { Project } from '@app/inherit-files' 2 | import { promises } from 'fs' 3 | import { posix, resolve } from 'path' 4 | 5 | const { readdir, stat, readFile } = promises 6 | const { join } = posix 7 | 8 | export const readFilesRecursively = async (path: string): Promise => { 9 | const result: Project = {} 10 | 11 | const entries = await readdir(path) 12 | const forEachEntry = async (entry: string) => { 13 | const entryPath = resolve(path, entry) 14 | const stats = await stat(entryPath) 15 | if (stats.isFile()) { 16 | const contents = await readFile(entryPath, 'utf8') 17 | result[entry] = contents 18 | return 19 | } 20 | 21 | if (stats.isDirectory()) { 22 | const recursive = await readFilesRecursively(entryPath) 23 | for (const [filename, contents] of Object.entries(recursive)) { 24 | result[join(entry, filename)] = contents 25 | } 26 | return 27 | } 28 | 29 | throw new Error() 30 | } 31 | await Promise.all(entries.map(forEachEntry)) 32 | 33 | return result 34 | } 35 | -------------------------------------------------------------------------------- /packages/example-packages/src/sub-directories.ts: -------------------------------------------------------------------------------- 1 | import { promises } from 'fs' 2 | import { resolve } from 'path' 3 | 4 | const { readdir, stat } = promises 5 | 6 | export const subDirectories = async (path: string): Promise => { 7 | const result: string[] = [] 8 | 9 | const entries = await readdir(path) 10 | const forEachEntry = async (entry: string) => { 11 | const stats = await stat(resolve(path, entry)) 12 | 13 | if (stats.isDirectory()) result.push(entry) 14 | } 15 | await Promise.all(entries.map(forEachEntry)) 16 | 17 | return result 18 | } 19 | -------------------------------------------------------------------------------- /packages/example-packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.dist.json", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "outDir": "dist" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/examples/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | setupFilesAfterEnv: [ 4 | 'jest-styled-components', 5 | '@testing-library/jest-dom/extend-expect', 6 | ], 7 | moduleNameMapper: { 8 | '\\.css$': 'identity-obj-proxy', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@app/examples", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest", 7 | "test:watch": "jest --watch" 8 | }, 9 | "peerDependencies": { 10 | "@types/history": "4.6.2", 11 | "@types/react": "16.9.49", 12 | "@types/react-dom": "16.9.8", 13 | "@types/react-router": "5.1.8", 14 | "@types/styled-components": "5.1.3", 15 | "formik": "2.2.0", 16 | "history": "4.9.0", 17 | "react": "16.13.1", 18 | "react-dom": "16.13.1", 19 | "react-is": "16.13.1", 20 | "react-router": "5.2.0", 21 | "react-router-dom": "5.2.0", 22 | "styled-components": "5.2.0" 23 | }, 24 | "devDependencies": { 25 | "@testing-library/dom": "7.26.0", 26 | "@testing-library/jest-dom": "5.11.4", 27 | "@testing-library/react": "11.0.2", 28 | "@testing-library/user-event": "12.1.3", 29 | "@types/history": "4.6.2", 30 | "@types/jest": "26.0.13", 31 | "@types/react": "16.9.49", 32 | "@types/react-dom": "16.9.8", 33 | "@types/react-router": "5.1.8", 34 | "@types/styled-components": "5.1.3", 35 | "formik": "2.2.0", 36 | "history": "4.9.0", 37 | "identity-obj-proxy": "3.0.0", 38 | "jest": "26.5.3", 39 | "jest-styled-components": "7.0.3", 40 | "react": "16.13.1", 41 | "react-dom": "16.13.1", 42 | "react-is": "16.13.1", 43 | "react-router": "5.2.0", 44 | "styled-components": "5.2.0", 45 | "ts-jest": "26.3.0", 46 | "typescript": "4.0.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/examples/src/best-business-logic/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-floating-promises": "off", 4 | "class-methods-use-this": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/examples/src/best-business-logic/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/best-context/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-non-null-assertion": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/examples/src/best-context/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/best-context/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/best-context/.test -------------------------------------------------------------------------------- /packages/examples/src/best-context/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`List should snapshot 1`] = ` 4 |
5 |
    6 |
  • 7 | hello 8 |
  • 9 |
  • 10 | world 11 |
  • 12 |
  • 13 | ( 14 | 3 15 | ) 16 | hello 17 |
  • 18 |
  • 19 | ( 20 | 4 21 | ) 22 | world 23 |
  • 24 |
25 |
26 | `; 27 | -------------------------------------------------------------------------------- /packages/examples/src/best-context/src/example.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render, RenderResult } from '@testing-library/react' 2 | import React from 'react' 3 | import example from './example' 4 | 5 | describe('List', () => { 6 | let component: RenderResult 7 | 8 | beforeEach(() => { 9 | component = render(<>{example}) 10 | }) 11 | 12 | afterEach(() => { 13 | cleanup() 14 | }) 15 | 16 | it('should snapshot', () => { 17 | expect(component.container).toMatchSnapshot() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/examples/src/best-performance/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/best-performance/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/best-performance/.test -------------------------------------------------------------------------------- /packages/examples/src/best-performance/src/example.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render, RenderResult, screen } from '@testing-library/react' 2 | import userEvent from '@testing-library/user-event' 3 | import React from 'react' 4 | import { ProbablyFaster, Slower } from './example' 5 | 6 | describe('Filter', () => { 7 | let slower: RenderResult 8 | let faster: RenderResult 9 | 10 | beforeEach(() => { 11 | slower = render() 12 | faster = render() 13 | }) 14 | 15 | afterEach(() => { 16 | cleanup() 17 | }) 18 | 19 | it('should match', () => { 20 | expect(slower.container.innerHTML).toBe(faster.container.innerHTML) 21 | }) 22 | 23 | describe('when filtering', () => { 24 | beforeEach(async () => { 25 | const [slowerInput, fasterInput] = screen.getAllByRole('textbox') 26 | await userEvent.type(slowerInput, 'i') 27 | await userEvent.type(fasterInput, 'i') 28 | }) 29 | 30 | it('should match', () => { 31 | expect(slower.container.innerHTML).toBe(faster.container.innerHTML) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/examples/src/best-prefer-primitive/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/best-prefer-primitive/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | 3 | export const BadFlexBox: FC<{ 4 | place?: { align?: string; justify?: string } 5 | }> = () => <> 6 | 7 | export const badFlexBox = ( 8 | children 9 | ) 10 | 11 | export const GoodFlexBox: FC<{ 12 | align?: string 13 | justify?: string 14 | }> = () => <> 15 | 16 | export const goodFlexBox = children 17 | 18 | export const Select: FC<{ options: string[] }> = () => <> 19 | 20 | const constantOptions = ['hello', 'world'] 21 | export const goodSelect = 23 | -------------------------------------------------------------------------------- /packages/examples/src/best-props/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/best-props/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, InputHTMLAttributes } from 'react' 2 | 3 | export const ClosedInput: FC<{ type?: 'text' | 'tel' }> = ({ 4 | type = 'text', 5 | }) => 6 | 7 | export const typeText = 8 | export const typeTel = 9 | // export const cannotAddEvents = {}} /> 10 | 11 | export const OpenInput: FC> = ({ 12 | type = 'text', 13 | ...props 14 | }) => 15 | 16 | export const canAddEvents = {}} /> 17 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-floating-promises": "off", 4 | "@typescript-eslint/no-empty-interface": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/class-bitcoin/.test -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Bitcoin should snapshot 1`] = ` 4 |
5 |
6 | 11 | 16 |
17 | 18 | loading price... 19 | 20 |
21 |
22 | `; 23 | 24 | exports[`Bitcoin when price resolves should snapshot 1`] = ` 25 |
26 |
27 | 32 | 37 |
38 | 39 | £ 40 | 12345 41 | 42 |
43 |
44 | `; 45 | 46 | exports[`Bitcoin when request rejects should snapshot 1`] = ` 47 |
48 |
49 | 54 | 59 |
60 | 61 | £ 62 | NaN 63 | 64 |
65 |
66 | `; 67 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/__snapshots__/fc-example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Bitcoin should snapshot 1`] = ` 4 |
5 |
6 | 11 | 16 |
17 | 18 | loading price... 19 | 20 |
21 |
22 | `; 23 | 24 | exports[`Bitcoin when price resolves should snapshot 1`] = ` 25 |
26 |
27 | 32 | 37 |
38 | 39 | £ 40 | 12345 41 | 42 |
43 |
44 | `; 45 | 46 | exports[`Bitcoin when request rejects should snapshot 1`] = ` 47 |
48 |
49 | 54 | 59 |
60 | 61 | £ 62 | NaN 63 | 64 |
65 |
66 | `; 67 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/bitcoin-api.ts: -------------------------------------------------------------------------------- 1 | const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) 2 | 3 | class BitcoinAPIImpl { 4 | private BASE_URL = 'https://api.coingecko.com' 5 | 6 | async getPrice(fiat: string): Promise { 7 | const coin = 'bitcoin' 8 | const result = await fetch(this.priceUrl(coin, fiat)) 9 | const json = (await result.json()) as Record> 10 | await delay(1000 * Math.random()) 11 | return Number(json[coin][fiat]) 12 | } 13 | 14 | private priceUrl(coin: string, fiat: string): string { 15 | return `${this.BASE_URL}/api/v3/simple/price?ids=${coin}&vs_currencies=${fiat}` 16 | } 17 | } 18 | 19 | export const BitcoinAPI = new BitcoinAPIImpl() 20 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/fc-example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' 2 | import { BitcoinAPI } from './bitcoin-api' 3 | 4 | export const Bitcoin: FC = () => { 5 | const [fiat, setFiat] = useState<'gbp' | 'usd'>('gbp') 6 | const setGbp = useCallback(() => setFiat('gbp'), []) 7 | const setUsd = useCallback(() => setFiat('usd'), []) 8 | 9 | const [price, setPrice] = useState(null) 10 | useEffect(() => { 11 | let shouldUpdate = true 12 | 13 | setPrice(null) 14 | BitcoinAPI.getPrice(fiat) 15 | .then((v) => shouldUpdate && setPrice(v)) 16 | .catch(() => shouldUpdate && setPrice(NaN)) 17 | 18 | return () => { 19 | shouldUpdate = false 20 | } 21 | }, [fiat]) 22 | 23 | const symbol = useMemo(() => ({ gbp: '£', usd: '$' }[fiat]), [fiat]) 24 | 25 | return ( 26 |
27 | 30 | 33 |
34 | {price == null ? ( 35 | loading price... 36 | ) : ( 37 | 38 | {symbol} 39 | {price} 40 | 41 | )} 42 |
43 | ) 44 | } 45 | 46 | export default 47 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/testing/deferred.ts: -------------------------------------------------------------------------------- 1 | export class Deferred { 2 | resolve!: (value?: T | PromiseLike) => void 3 | 4 | reject!: (reason?: unknown) => void 5 | 6 | promise = new Promise((resolve, reject) => { 7 | this.resolve = resolve 8 | this.reject = reject 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /packages/examples/src/class-bitcoin/src/testing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './deferred' 2 | -------------------------------------------------------------------------------- /packages/examples/src/class-simple/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/class-simple/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/class-simple/.test -------------------------------------------------------------------------------- /packages/examples/src/class-simple/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Counter should snapshot 1`] = ` 4 |
5 |
6 | 11 | 12 | 13 | 0 14 | 15 | 16 | 21 |
22 |
23 | `; 24 | -------------------------------------------------------------------------------- /packages/examples/src/class-simple/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ReactNode } from 'react' 2 | 3 | export interface CounterProps { 4 | defaultValue: number 5 | } 6 | 7 | interface CounterState { 8 | value: number 9 | } 10 | 11 | export class Counter extends Component { 12 | static defaultProps = { defaultValue: 0 } 13 | 14 | state = { value: this.props.defaultValue } 15 | 16 | decrement = (): void => this.setState(({ value }) => ({ value: value - 1 })) 17 | 18 | increment = (): void => this.setState(({ value }) => ({ value: value + 1 })) 19 | 20 | badIncrement(): void { 21 | // `this` is not bound 22 | this.setState(({ value }) => ({ value: value + 1 })) 23 | } 24 | 25 | render = (): ReactNode => { 26 | const { value } = this.state 27 | 28 | return ( 29 |
30 | 33 | {value} 34 | 37 |
38 | ) 39 | } 40 | } 41 | 42 | export default ( 43 | <> 44 | 45 | 46 | 47 | ) 48 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateful/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateful/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/components-stateful/.test -------------------------------------------------------------------------------- /packages/examples/src/components-stateful/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Pizza should snapshot 1`] = ` 4 |
5 |
6 |

7 | Choose a pizza topping: 8 |

9 |
10 | 15 | 20 | 25 |
26 |

27 | Chosen topping: 28 | none 29 |

30 |
31 |
32 | `; 33 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateful/src/example.test.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | cleanup, 3 | fireEvent, 4 | render, 5 | RenderResult, 6 | } from '@testing-library/react' 7 | import React from 'react' 8 | import { Pizza } from './example' 9 | 10 | describe('Pizza', () => { 11 | let component: RenderResult 12 | 13 | beforeEach(() => { 14 | component = render() 15 | }) 16 | 17 | afterEach(() => { 18 | cleanup() 19 | }) 20 | 21 | it('should snapshot', () => { 22 | expect(component.container).toMatchSnapshot() 23 | }) 24 | 25 | it('should default to none', () => { 26 | expect(component.container).toHaveTextContent('Chosen topping: none') 27 | }) 28 | 29 | const choices = ['tomato sauce', 'bbq sauce', 'garlic sauce'] 30 | describe.each(choices)('when choosing %s', (choice) => { 31 | beforeEach(() => { 32 | const button = component.getByText(choice) 33 | fireEvent.click(button) 34 | }) 35 | 36 | it('should update choice', () => { 37 | expect(component.container).toHaveTextContent(`Chosen topping: ${choice}`) 38 | }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateful/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from 'react' 2 | 3 | export const Pizza: FC = () => { 4 | const [topping, setTopping] = useState('none') 5 | return ( 6 |
7 |

Choose a pizza topping:

8 |
9 | 12 | 15 | 18 |
19 |

Chosen topping: {topping}

20 |
21 | ) 22 | } 23 | 24 | export default 25 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateless/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/components-stateless/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | 3 | export const Button: FC = ({ children }) => ( 4 | 5 | ) 6 | 7 | export default 8 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/.extends: -------------------------------------------------------------------------------- 1 | react 2 | with-css-module 3 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/context-complex/.test -------------------------------------------------------------------------------- /packages/examples/src/context-complex/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ThemeSwitcher example should snapshot 1`] = ` 4 |
5 | 19 |
20 | 26 |
27 | `; 28 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/src/button.default.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | margin: 0.25em; 3 | padding: 0.5em 1em; 4 | background: #8be9fd; 5 | color: #21222c; 6 | border: none; 7 | border-radius: 4px; 8 | transition: transform 0.2s cubic-bezier(0.68, -0.6, 0.32, 1.6); 9 | } 10 | .button:focus { 11 | outline: 2px solid #8be9fd; 12 | outline-offset: 2px; 13 | } 14 | .button:hover { 15 | transform: scale(1.05); 16 | } 17 | .button:active { 18 | transform: scale(0.95); 19 | } 20 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/src/button.green.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | margin: 0.25em; 3 | padding: 0.25em 1em; 4 | background: #50fa7b; 5 | border: none; 6 | --clip-size: 0.7em; 7 | clip-path: polygon( 8 | 100% 0, 9 | 100% calc(100% - var(--clip-size)), 10 | calc(100% - var(--clip-size)) 100%, 11 | 0 100%, 12 | 0 var(--clip-size), 13 | var(--clip-size) 0 14 | ); 15 | transition: clip-path 0.1s cubic-bezier(0.785, 0.135, 0.15, 0.86); 16 | outline: none; 17 | } 18 | .button:focus { 19 | text-decoration: underline; 20 | } 21 | .button:hover { 22 | --clip-size: 0.5em; 23 | } 24 | .button:active { 25 | --clip-size: 0.3em; 26 | } 27 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/src/button.red.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | background: none; 3 | border: 2px solid #ff5555; 4 | color: inherit; 5 | margin: 0.5em; 6 | padding: 1em; 7 | border-radius: 999px; 8 | transition: 0.3s cubic-bezier(0.83, 0, 0.17, 1); 9 | transition-property: box-shadow, transform; 10 | } 11 | .button:focus { 12 | outline: 1px dotted #f1fa8c; 13 | outline-offset: -1em; 14 | } 15 | .button:hover { 16 | box-shadow: 3px 3px #f1fa8c; 17 | transform: translate(-3px, -3px); 18 | } 19 | .button:active { 20 | box-shadow: 1px 1px #f1fa8c; 21 | transform: translate(-1px, -1px); 22 | } 23 | -------------------------------------------------------------------------------- /packages/examples/src/context-complex/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, FC, useContext, useState } from 'react' 2 | import defaultClasses from './button.default.module.css' 3 | import greenClasses from './button.green.module.css' 4 | import redClasses from './button.red.module.css' 5 | 6 | export type ThemeContextType = 'default' | 'red' | 'green' 7 | export const ThemeContext = createContext('default') 8 | 9 | export const Button: FC = () => { 10 | const theme = useContext(ThemeContext) 11 | const classes = { 12 | default: defaultClasses, 13 | red: redClasses, 14 | green: greenClasses, 15 | }[theme] 16 | 17 | return ( 18 | 21 | ) 22 | } 23 | 24 | export const ThemeSwitcher: FC = ({ children }) => { 25 | const [theme, setTheme] = useState('default') 26 | 27 | return ( 28 | <> 29 | 37 |
38 | 39 | {children} 40 | 41 | 42 | ) 43 | } 44 | 45 | export default ( 46 | 47 | 16 | 17 | 18 | `; 19 | 20 | exports[`Dropdown when opened should snapshot 1`] = ` 21 |
22 |
25 | 33 |
36 | content 37 |
38 |
39 |
40 | `; 41 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-click-out/src/example.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | } 4 | 5 | .toggle { 6 | width: 100%; 7 | } 8 | 9 | .content { 10 | border: 2px solid #ff5555; 11 | position: absolute; 12 | left: 0; 13 | right: 0; 14 | background: #282a36; 15 | color: #f8f8f2; 16 | } 17 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-formik/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-non-null-assertion": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-formik/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-formik/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/custom-hooks-formik/.test -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-navigation/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-non-null-assertion": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-navigation/.extends: -------------------------------------------------------------------------------- 1 | react 2 | with-css-module 3 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-navigation/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/custom-hooks-navigation/.test -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-navigation/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Tabs should snapshot 1`] = ` 4 |
5 |
6 |
7 | 14 | 21 | 28 | 35 |
36 |
37 | 40 |
41 |
42 |
43 | `; 44 | -------------------------------------------------------------------------------- /packages/examples/src/custom-hooks-navigation/src/example.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | border: 4px solid #282a36; 3 | outline: none; 4 | } 5 | 6 | .button:focus { 7 | border-color: #ff5555; 8 | } 9 | -------------------------------------------------------------------------------- /packages/examples/src/forms-events/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/forms-events/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/forms-events/.test -------------------------------------------------------------------------------- /packages/examples/src/forms-events/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Events should snapshot 1`] = ` 4 |
5 |
6 | 10 |
11 |       last event: 
12 |       N/A
13 |     
14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /packages/examples/src/forms-events/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from 'react' 2 | 3 | export const Events: FC = () => { 4 | const [lastEvent, setLastEvent] = useState('N/A') 5 | return ( 6 |
7 | setLastEvent('onFocus')} 11 | onBlur={() => setLastEvent('onBlur')} 12 | onCopy={() => setLastEvent('onCopy')} 13 | onCut={() => setLastEvent('onCut')} 14 | onPaste={() => setLastEvent('onPaste')} 15 | onKeyDown={() => setLastEvent('onKeyDown')} 16 | onKeyUp={() => setLastEvent('onKeyUp')} 17 | /> 18 |
last event: {lastEvent}
19 |
20 | ) 21 | } 22 | 23 | export default 24 | -------------------------------------------------------------------------------- /packages/examples/src/forms-formik/.extends: -------------------------------------------------------------------------------- 1 | react 2 | with-formik 3 | -------------------------------------------------------------------------------- /packages/examples/src/forms-formik/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/forms-formik/.test -------------------------------------------------------------------------------- /packages/examples/src/forms-formik/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SimpleFormik should snapshot 1`] = ` 4 |
5 |
9 |
10 | 17 |
18 |
19 | 27 |
28 |
29 | 39 |
40 | 45 |
46 |       {
47 |   "username": "",
48 |   "password": "",
49 |   "terms": false,
50 |   "submitted": false
51 | }
52 |     
53 |
54 |
55 | `; 56 | -------------------------------------------------------------------------------- /packages/examples/src/forms-formik/src/example.tsx: -------------------------------------------------------------------------------- 1 | import { Field, FieldProps, Form, Formik } from 'formik' 2 | import React, { FC } from 'react' 3 | import { UserAPI } from './user-api' 4 | 5 | export const SimpleFormik: FC = () => { 6 | return ( 7 | { 15 | setFieldValue('submitted', true) 16 | try { 17 | await UserAPI.register({ username, password }) 18 | // redirect to profile 19 | } catch { 20 | // show error 21 | } 22 | }} 23 | > 24 |
25 |
26 | 30 |
31 |
32 | 36 |
37 |
38 | 42 |
43 | 44 | 45 | {({ form }: FieldProps) => ( 46 |
{JSON.stringify(form.values, null, '  ')}
47 | )} 48 |
49 |
50 |
51 | ) 52 | } 53 | 54 | export default 55 | -------------------------------------------------------------------------------- /packages/examples/src/forms-formik/src/user-api.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: string 3 | username: string 4 | } 5 | 6 | interface RegisterArgs { 7 | username: string 8 | password: string 9 | } 10 | 11 | const MockUserAPI = { 12 | register({ username }: RegisterArgs): Promise { 13 | return Promise.resolve({ username, id: (Math.random() * 10000).toFixed(0) }) 14 | }, 15 | } 16 | 17 | export const UserAPI = MockUserAPI 18 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-form/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-form/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/forms-simple-form/.test -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-form/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SimpleForm should snapshot 1`] = ` 4 |
5 |
8 |
9 | 17 |
18 |
19 | 27 |
28 |
29 | 38 |
39 | 44 |
45 |       {
46 |   "username": "",
47 |   "password": "",
48 |   "terms": false,
49 |   "submitted": false
50 | }
51 |     
52 |
53 |
54 | `; 55 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-form/src/user-api.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: string 3 | username: string 4 | } 5 | 6 | interface RegisterArgs { 7 | username: string 8 | password: string 9 | } 10 | 11 | const MockUserAPI = { 12 | register({ username }: RegisterArgs): Promise { 13 | return Promise.resolve({ username, id: (Math.random() * 10000).toFixed(0) }) 14 | }, 15 | } 16 | 17 | export const UserAPI = MockUserAPI 18 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-input/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-input/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/forms-simple-input/.test -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-input/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SimpleInput should snapshot 1`] = ` 4 |
5 |
6 | 13 |
14 |       default value
15 |     
16 | 21 |
22 |
23 | `; 24 | -------------------------------------------------------------------------------- /packages/examples/src/forms-simple-input/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, FC, useState } from 'react' 2 | 3 | export const SimpleInput: FC = () => { 4 | const [value, setValue] = useState('default value') 5 | const onChange = (event: ChangeEvent) => { 6 | setValue(event.target.value) 7 | } 8 | 9 | return ( 10 |
11 | 15 |
{value}
16 | 19 |
20 | ) 21 | } 22 | 23 | export default 24 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/no-non-null-assertion": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hoc-theme/.test -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/__snapshots__/themes.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`themes dark should snapshot 1`] = ` 4 |
5 |
 6 |     {
 7 |   "color": {
 8 |     "background": "#282a36",
 9 |     "foreground": "#f8f8f2"
10 |   }
11 | }
12 |   
13 |
14 | `; 15 | 16 | exports[`themes light should snapshot 1`] = ` 17 |
18 |
19 |     {
20 |   "color": {
21 |     "background": "#f8f8f2",
22 |     "foreground": "#282a36"
23 |   }
24 | }
25 |   
26 |
27 | `; 28 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/__snapshots__/with-theme.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`withTheme dark should inject theme prop 1`] = ` 4 | Array [ 5 | Array [ 6 | Object { 7 | "theme": Object { 8 | "color": Object { 9 | "background": "#282a36", 10 | "foreground": "#f8f8f2", 11 | }, 12 | }, 13 | }, 14 | ], 15 | ] 16 | `; 17 | 18 | exports[`withTheme light should inject theme prop 1`] = ` 19 | Array [ 20 | Array [ 21 | Object { 22 | "theme": Object { 23 | "color": Object { 24 | "background": "#f8f8f2", 25 | "foreground": "#282a36", 26 | }, 27 | }, 28 | }, 29 | ], 30 | ] 31 | `; 32 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/index.ts: -------------------------------------------------------------------------------- 1 | export * from './themes' 2 | export * from './with-theme' 3 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/internal/theme-context.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | 3 | export interface ThemeContextType { 4 | color: { 5 | background: string 6 | foreground: string 7 | } 8 | } 9 | 10 | export const ThemeContext = createContext(null!) 11 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/themes.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render, RenderResult } from '@testing-library/react' 2 | import React, { FC, useContext } from 'react' 3 | import { ThemeContext } from './internal/theme-context' 4 | import { DarkTheme, LightTheme } from './themes' 5 | 6 | describe('themes', () => { 7 | let component: RenderResult 8 | 9 | const Test: FC = () => { 10 | const theme = useContext(ThemeContext) 11 | 12 | return
{JSON.stringify(theme, null, '  ')}
13 | } 14 | 15 | afterEach(() => { 16 | cleanup() 17 | }) 18 | 19 | describe('dark', () => { 20 | beforeEach(() => { 21 | component = render( 22 | 23 | 24 | , 25 | ) 26 | }) 27 | 28 | it('should snapshot', () => { 29 | expect(component.container).toMatchSnapshot() 30 | }) 31 | }) 32 | 33 | describe('light', () => { 34 | beforeEach(() => { 35 | component = render( 36 | 37 | 38 | , 39 | ) 40 | }) 41 | 42 | it('should snapshot', () => { 43 | expect(component.container).toMatchSnapshot() 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/themes.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | import { ThemeContext, ThemeContextType } from './internal/theme-context' 3 | 4 | const darkTheme: ThemeContextType = { 5 | color: { 6 | background: '#282a36', 7 | foreground: '#f8f8f2', 8 | }, 9 | } 10 | export const DarkTheme: FC = ({ children }) => ( 11 | {children} 12 | ) 13 | 14 | const lightTheme: ThemeContextType = { 15 | color: { 16 | background: '#f8f8f2', 17 | foreground: '#282a36', 18 | }, 19 | } 20 | export const LightTheme: FC = ({ children }) => ( 21 | {children} 22 | ) 23 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/with-theme.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render } from '@testing-library/react' 2 | import React, { Component } from 'react' 3 | import { DarkTheme, LightTheme } from './themes' 4 | import { PropsWithTheme, withTheme } from './with-theme' 5 | 6 | describe('withTheme', () => { 7 | let propsSpy: jest.Mock 8 | 9 | const Test = withTheme( 10 | class extends Component { 11 | static displayName = 'Test' 12 | 13 | componentDidMount = () => { 14 | propsSpy(this.props) 15 | } 16 | 17 | render = () => { 18 | return null 19 | } 20 | }, 21 | ) 22 | 23 | beforeEach(() => { 24 | propsSpy = jest.fn() 25 | }) 26 | 27 | afterEach(() => { 28 | cleanup() 29 | }) 30 | 31 | describe('dark', () => { 32 | beforeEach(() => { 33 | render( 34 | 35 | 36 | , 37 | ) 38 | }) 39 | 40 | it('should inject theme prop', () => { 41 | expect(propsSpy.mock.calls).toMatchSnapshot() 42 | }) 43 | }) 44 | 45 | describe('light', () => { 46 | beforeEach(() => { 47 | render( 48 | 49 | 50 | , 51 | ) 52 | }) 53 | 54 | it('should inject theme prop', () => { 55 | expect(propsSpy.mock.calls).toMatchSnapshot() 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /packages/examples/src/hoc-theme/src/theme-library/with-theme.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ComponentType, ReactNode } from 'react' 2 | import { ThemeContext, ThemeContextType } from './internal/theme-context' 3 | 4 | export interface PropsWithTheme { 5 | theme: ThemeContextType 6 | } 7 | export type PropsWithoutTheme = Omit 8 | 9 | export const withTheme = ( 10 | WrappedComponent: ComponentType, 11 | ): ComponentType> => { 12 | type WithThemeComponentProps = PropsWithoutTheme 13 | 14 | return class WithThemeComponent extends Component { 15 | static displayName = `withTheme(${ 16 | WrappedComponent.displayName || WrappedComponent.name || 'Component' 17 | })` 18 | 19 | private themeContextConsumer = (theme: ThemeContextType): ReactNode => { 20 | const props = { ...this.props, theme } as TProps 21 | return 22 | } 23 | 24 | render = (): ReactNode => { 25 | return ( 26 | 27 | {this.themeContextConsumer} 28 | 29 | ) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-effect/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-effect/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-effect/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-effect/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`LocaleClock default locale should snapshot 1`] = ` 4 |
5 |

6 | 12:34:00 PM 7 |

8 |
9 | `; 10 | 11 | exports[`ToggleLocale should snapshot 1`] = ` 12 |
13 | 18 |

19 | current time in "en-GB" locale 20 |

21 |
22 | `; 23 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-effect/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect, useState } from 'react' 2 | 3 | export const LocaleClock: FC<{ locale?: string }> = ({ locale = 'en-US' }) => { 4 | const [timeString, setTimeString] = useState() 5 | useEffect(() => { 6 | const update = () => { 7 | setTimeString(new Date().toLocaleTimeString(locale)) 8 | } 9 | update() 10 | 11 | const interval = window.setInterval(update, 500) 12 | 13 | return () => { 14 | window.clearInterval(interval) 15 | } 16 | }, [locale]) 17 | 18 | return

{timeString}

19 | } 20 | 21 | export const ToggleLocale: FC = () => { 22 | const [locale, setLocale] = useState('en-GB') 23 | const toggleLocale = () => { 24 | setLocale(locale === 'en-GB' ? 'th-TH-u-nu-thai' : 'en-GB') 25 | } 26 | 27 | return ( 28 | <> 29 | 32 | 33 | 34 | ) 35 | } 36 | 37 | export default 38 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-imperative-handle/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-imperative-handle/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-imperative-handle/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-imperative-handle/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`FocusableInput should snapshot 1`] = ` 4 |
5 |
6 | 12 |
13 |
14 | this field is required 15 | 20 |
21 |
22 | `; 23 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-imperative-handle/src/example.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render, RenderResult } from '@testing-library/react' 2 | import userEvent from '@testing-library/user-event' 3 | import React, { FC, useRef } from 'react' 4 | import { Error, Focusable, FocusableInput } from './example' 5 | 6 | describe('FocusableInput', () => { 7 | let component: RenderResult 8 | let input: HTMLElement 9 | let button: HTMLElement 10 | 11 | const Test: FC = () => { 12 | const focusableRef = useRef(null) 13 | 14 | return ( 15 | <> 16 | 17 | this field is required 18 | 19 | ) 20 | } 21 | 22 | beforeEach(() => { 23 | component = render() 24 | input = component.getByRole('textbox') 25 | button = component.getByRole('button') 26 | }) 27 | 28 | afterEach(() => { 29 | cleanup() 30 | }) 31 | 32 | it('should snapshot', () => { 33 | expect(component.container).toMatchSnapshot() 34 | }) 35 | 36 | it('should not be focused', () => { 37 | expect(input).not.toHaveFocus() 38 | }) 39 | 40 | describe('when focusing field', () => { 41 | beforeEach(() => { 42 | userEvent.click(button) 43 | }) 44 | 45 | it('should be focused', () => { 46 | expect(input).toHaveFocus() 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-imperative-handle/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | FC, 3 | Ref, 4 | RefObject, 5 | useCallback, 6 | useImperativeHandle, 7 | useRef, 8 | } from 'react' 9 | 10 | export interface Focusable { 11 | focus(): void 12 | } 13 | 14 | export const FocusableInput: FC<{ focusable?: Ref }> = ({ 15 | focusable, 16 | }) => { 17 | const inputRef = useRef(null) 18 | const focus = useCallback(() => { 19 | inputRef.current?.focus() 20 | }, []) 21 | useImperativeHandle(focusable, () => ({ focus }), [focus]) 22 | 23 | return ( 24 |
25 | 29 |
30 | ) 31 | } 32 | 33 | export const Error: FC<{ target: RefObject }> = ({ 34 | target, 35 | children, 36 | }) => { 37 | const onClick = useCallback(() => { 38 | target.current?.focus() 39 | }, [target]) 40 | return ( 41 |
42 | {children} 43 | 46 |
47 | ) 48 | } 49 | 50 | const Example: FC = () => { 51 | const focusableRef = useRef(null) 52 | 53 | return ( 54 | <> 55 | 56 | this field is required 57 | 58 | ) 59 | } 60 | 61 | export default 62 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-memo/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-memo/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-memo/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-memo/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Fib 10 should snapshot 1`] = ` 4 |
5 |
 6 |     10
 7 |     -th fibonacci number: 
 8 |     55
 9 |   
10 |
11 | `; 12 | 13 | exports[`Fib 20 should snapshot 1`] = ` 14 |
15 |
16 |     20
17 |     -th fibonacci number: 
18 |     6765
19 |   
20 |
21 | `; 22 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-memo/src/example.test.tsx: -------------------------------------------------------------------------------- 1 | import { cleanup, render, RenderResult } from '@testing-library/react' 2 | import React from 'react' 3 | import { Fib } from './example' 4 | 5 | describe('Fib', () => { 6 | let component: RenderResult 7 | 8 | describe('10', () => { 9 | beforeEach(() => { 10 | component = render() 11 | }) 12 | 13 | afterEach(() => { 14 | cleanup() 15 | }) 16 | 17 | it('should snapshot', () => { 18 | expect(component.container).toMatchSnapshot() 19 | }) 20 | }) 21 | 22 | describe('20', () => { 23 | beforeEach(() => { 24 | component = render() 25 | }) 26 | 27 | afterEach(() => { 28 | cleanup() 29 | }) 30 | 31 | it('should snapshot', () => { 32 | expect(component.container).toMatchSnapshot() 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-memo/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo } from 'react' 2 | 3 | const fib = (n: number): number => { 4 | if (n <= 2) return 1 5 | 6 | return fib(n - 1) + fib(n - 2) 7 | } 8 | 9 | export const Fib: FC<{ n: number }> = ({ n }) => { 10 | const f = useMemo(() => fib(n), [n]) 11 | 12 | return ( 13 |
14 |       {n}-th fibonacci number: {f}
15 |     
16 | ) 17 | } 18 | 19 | export default 20 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-reducer/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-reducer/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-reducer/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-reducer/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Counter should snapshot 1`] = ` 4 |
5 | Count: 6 | 0 7 | 12 | 17 |
18 | `; 19 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-reducer/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, Reducer, useCallback, useReducer } from 'react' 2 | 3 | interface State { 4 | count: number 5 | } 6 | interface IncrementAction { 7 | type: 'increment' 8 | } 9 | interface DecrementAction { 10 | type: 'decrement' 11 | } 12 | type Action = IncrementAction | DecrementAction 13 | 14 | const reducer: Reducer = (state, action) => { 15 | switch (action.type) { 16 | case 'increment': 17 | return { count: state.count + 1 } 18 | case 'decrement': 19 | return { count: state.count - 1 } 20 | default: 21 | throw new Error() 22 | } 23 | } 24 | 25 | export const Counter: FC = () => { 26 | const [state, dispatch] = useReducer(reducer, { count: 0 }) 27 | const decrement = useCallback(() => dispatch({ type: 'decrement' }), []) 28 | const increment = useCallback(() => dispatch({ type: 'increment' }), []) 29 | return ( 30 | <> 31 | Count: {state.count} 32 | 35 | 38 | 39 | ) 40 | } 41 | 42 | export default 43 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref-alt/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref-alt/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-ref-alt/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref-alt/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SelfDestruct should snapshot 1`] = ` 4 |
5 | 10 | 15 |

16 | this message will destroy itself in 2 seconds 17 |

18 |
19 | `; 20 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref-alt/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useRef, useState } from 'react' 2 | 3 | export const SelfDestruct: FC = () => { 4 | const [destroyed, setDestroyed] = useState(false) 5 | const timeoutRef = useRef() 6 | 7 | const cancel = () => { 8 | window.clearTimeout(timeoutRef.current) 9 | timeoutRef.current = undefined 10 | } 11 | const start = () => { 12 | cancel() 13 | timeoutRef.current = window.setTimeout(() => { 14 | setDestroyed(true) 15 | }, 2000) 16 | } 17 | 18 | return destroyed ? null : ( 19 | <> 20 | 23 | 26 |

this message will destroy itself in 2 seconds

27 | 28 | ) 29 | } 30 | 31 | export default 32 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref/.extends: -------------------------------------------------------------------------------- 1 | react 2 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactbyexample/react-by-example/105c004b58bc8a6ffe2d12ec0c8fe8d9f5b19dbd/packages/examples/src/hooks-use-ref/.test -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref/src/__snapshots__/example.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PlayPause should snapshot 1`] = ` 4 |
5 |
18 | `; 19 | -------------------------------------------------------------------------------- /packages/examples/src/hooks-use-ref/src/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useRef } from 'react' 2 | 3 | export const PlayPause: FC = () => { 4 | const videoRef = useRef(null) 5 | const playPause = async () => { 6 | const video = videoRef.current as HTMLVideoElement 7 | if (video.paused) { 8 | await video.play() 9 | } else { 10 | video.pause() 11 | } 12 | } 13 | 14 | return ( 15 | <> 16 |