30 | This is just a demo for DevTools.{' '}
31 | {window.devToolsEnabled ? (
32 | <>
33 | DevTools are enabled. The UI for the DevTools appears in the
34 | bottom left. Add{' '}
35 |
36 | "?dev-tools=false"
37 | {' '}
38 | to the URL to explicitely disable them (though in production they
39 | are disabled by default)
40 | >
41 | ) : (
42 | <>
43 | DevTools are disabled. Add{' '}
44 |
45 | "?dev-tools=true"
46 | {' '}
47 | to the URL to explicitely enable them (though in development they
48 | are enabled by default). The DevTools UI will appear in the bottom
49 | left.
50 | >
51 | )}
52 |
55 | Notice that these work even in production! But it doesn't cost
56 | anything to have them available for users who don't use them. Read
57 | the blog post to learn how.
58 |
58 | )
59 | }
60 |
61 | // add dev tools UI to the page
62 | const devToolsRoot = document.createElement('div')
63 | document.body.appendChild(devToolsRoot)
64 | ReactDOM.render(, devToolsRoot)
65 | }
66 |
67 | export {install}
68 |
--------------------------------------------------------------------------------
/src/dev-tools/dev-tools.local-example.js:
--------------------------------------------------------------------------------
1 | // if you want to have custom dev tools, create a file right here called
2 | // dev-tools.local.js.
3 | // because the .gitignore lists *.local.* as ignored you can change anything
4 | // you want in your local dev-tools file and it won't impact anyone else.
5 |
6 | // Here's an example of some of the things you could do:
7 | import React from 'react'
8 | import {screen, waitForElementToBeRemoved} from '@testing-library/react'
9 | import userEvent from '@testing-library/user-event'
10 | import {enable} from './feature-toggles'
11 |
12 | // If I want to make sure that tacos is always enabled locally, I can uncomment this:
13 | enable('tacos')
14 |
15 | // you can do whatever hackery you want in here.
16 | // Like you could use React Testing Library to fill out the username add password on load:
17 | async function automaticallyLogin() {
18 | try {
19 | await userEvent.type(await screen.findByLabelText(/username/i), 'FAKE_USER')
20 | await userEvent.type(
21 | await screen.findByLabelText(/password/i),
22 | 'FAKE_PASSWORD',
23 | )
24 | await userEvent.click(await screen.findByText(/submit/i))
25 | await waitForElementToBeRemoved(() => screen.getByText(/loading/i))
26 | } catch (error) {
27 | //ignore the error because we're probably already logged in so no need
28 | }
29 | }
30 |
31 | automaticallyLogin()
32 |
33 | // in the past I'd also have a history listener that would automatically execute
34 | // code when I navigated to a specific path (to auto-fill a large form I was
35 | // working on).
36 |
37 | // This is an optional component I can export to add a tool to the DevTools UI
38 | export default () =>
Local dev tools!
39 |
--------------------------------------------------------------------------------
/src/dev-tools/feature-toggles.js:
--------------------------------------------------------------------------------
1 | import featureToggles from '../feature-toggles'
2 |
3 | const key = 'feature-toggles'
4 |
5 | // update featureToggles with what's in localStorage
6 | try {
7 | Object.assign(featureToggles, JSON.parse(window.localStorage.getItem(key)))
8 | } catch (error) {
9 | window.localStorage.removeItem(key)
10 | }
11 |
12 | const persist = () =>
13 | window.localStorage.setItem(key, JSON.stringify(featureToggles))
14 |
15 | function enable(name) {
16 | console.log(featureToggles, name, 'enabling')
17 | featureToggles[name] = true
18 | persist()
19 | }
20 |
21 | function disable(name) {
22 | console.log(featureToggles, name, 'disabling')
23 | featureToggles[name] = false
24 | persist()
25 | }
26 |
27 | export default featureToggles
28 | export {enable, disable}
29 |
--------------------------------------------------------------------------------
/src/dev-tools/load.js:
--------------------------------------------------------------------------------
1 | function loadDevTools(callback) {
2 | // this allows you to explicitly disable it in development for example
3 | const explicitlyDisabled =
4 | window.location.search.includes('dev-tools=false') ||
5 | window.localStorage.getItem('dev-tools') === 'false'
6 |
7 | const explicitlyEnabled =
8 | window.location.search.includes('dev-tools=true') ||
9 | window.localStorage.getItem('dev-tools') === 'true'
10 |
11 | // we want it enabled by default everywhere but production and we also want
12 | // to support the dev tools in production (to make us more productive triaging production issues).
13 | // you can enable the DevTools via localStorage or the query string.
14 | if (
15 | !explicitlyDisabled &&
16 | (process.env.NODE_ENV === 'development' || explicitlyEnabled)
17 | ) {
18 | // use a dynamic import so the dev-tools code isn't bundled with the regular
19 | // app code so we don't worry about bundle size.
20 | import('./dev-tools')
21 | .then(devTools => devTools.install())
22 | .finally(callback)
23 | } else {
24 | // if we don't need the DevTools, call the callback immediately.
25 | callback()
26 | }
27 | }
28 |
29 | export default loadDevTools
30 |
--------------------------------------------------------------------------------
/src/feature-toggles.js:
--------------------------------------------------------------------------------
1 | // APP_CONFIG is set via the `config.js` script that's in /public
2 | const featureToggles = window.APP_CONFIG.featureToggles
3 |
4 | export default featureToggles
5 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import loadDevTools from './dev-tools/load'
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import './index.css'
5 | import App from './App'
6 |
7 | // load and install the dev tools (if they need to be)
8 | // and when that's done, let's render the app
9 | // NOTE: if we don't need to install the devtools, then the callback
10 | // is called synchronously so there's no penalty for including this
11 | // in production.
12 | loadDevTools(() => {
13 | ReactDOM.render(, document.getElementById('root'))
14 | })
15 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect'
6 |
7 | window.APP_CONFIG = {featureToggles: {}}
8 |
--------------------------------------------------------------------------------