/storyshots/index.js\"",
30 | "coverage:merge": "istanbul-merge --out coverage/merged/coverage-final.json ./coverage/unit/coverage-final.json ./coverage/visual-regression/coverage-final.json ./coverage/integration/coverage-final.json",
31 | "coverage:merge-report": "nyc report --reporter=lcov --reporter=text --temp-dir=./coverage/merged --report-dir=./coverage/merged"
32 | },
33 | "jest": {
34 | "collectCoverageFrom": [
35 | "src/**/*.js",
36 | "!**/*.test.js",
37 | "!**/test.js",
38 | "!**/*.stories.js",
39 | "!**/stories.js"
40 | ]
41 | },
42 | "nyc": {
43 | "report-dir": "coverage/integration",
44 | "reporter": ["text", "json", "lcov"],
45 | "all": true,
46 | "include": [
47 | "src/**/*.js"
48 | ],
49 | "exclude": [
50 | "**/*.test.js",
51 | "**/test.js",
52 | "**/*.stories.js",
53 | "**/stories.js",
54 | "src/setupTests.js"
55 | ]
56 | },
57 | "eslintConfig": {
58 | "extends": "react-app"
59 | },
60 | "browserslist": {
61 | "production": [
62 | ">0.2%",
63 | "not dead",
64 | "not op_mini all"
65 | ],
66 | "development": [
67 | "last 1 chrome version",
68 | "last 1 firefox version",
69 | "last 1 safari version"
70 | ]
71 | },
72 | "devDependencies": {
73 | "@cypress/code-coverage": "^3.5.1",
74 | "@cypress/instrument-cra": "^1.1.1",
75 | "@storybook/addon-actions": "^5.3.18",
76 | "@storybook/addon-links": "^5.3.18",
77 | "@storybook/addon-storyshots": "^5.3.18",
78 | "@storybook/addons": "^5.3.18",
79 | "@storybook/preset-create-react-app": "^2.1.1",
80 | "@storybook/react": "^5.3.18",
81 | "cypress": "^4.4.0",
82 | "istanbul-lib-coverage": "^3.0.0",
83 | "istanbul-merge": "^1.1.1",
84 | "nyc": "^15.0.1",
85 | "react-test-renderer": "^16.13.1"
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/penx/storybook-code-coverage/bd07752ffa617a601c78dbbd0114edd9055fb16e/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/penx/storybook-code-coverage/bd07752ffa617a601c78dbbd0114edd9055fb16e/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/penx/storybook-code-coverage/bd07752ffa617a601c78dbbd0114edd9055fb16e/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/application/About/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const About = () => (
4 |
5 |
About
6 |
About page
7 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/application/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useToggle } from "../utilities/useToggle";
3 | import { Toggle } from "../design-system/Toggle";
4 | import { GlobalStyles } from "../design-system/GlobalStyles";
5 | import { Switch, Route, Link } from "react-router-dom";
6 | import { About } from "./About";
7 |
8 | const Users = React.lazy(() => import('./Users'));
9 |
10 | function App() {
11 | const { on: darkModeOn, toggle: toggleDarkMode } = useToggle();
12 | return (
13 |
14 | Example Application
15 | Dark Mode:
16 |
17 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Home
44 | Home page
45 |
46 | {darkModeOn && (
47 |
48 |
49 |
50 | )}
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | const DarkModePage = () => (
58 | <>
59 | Hidden Page
60 | This route is only available in dark mode
61 | >
62 | );
63 |
64 | export default App;
65 |
--------------------------------------------------------------------------------
/src/application/Users/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const Users = () => (
4 |
5 |
Users
6 |
Users page
7 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/design-system/Alert/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Alert = () =>
4 | This component is not used by the application and is not in storybook.
5 |
--------------------------------------------------------------------------------
/src/design-system/GlobalStyles/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ThemeProvider, createGlobalStyle } from "styled-components";
3 |
4 | const defaultTheme = {
5 | };
6 |
7 | const darkTheme = {
8 | primary: "black",
9 | text: "white",
10 | };
11 |
12 | const GlobalStyle = createGlobalStyle`
13 | body {
14 | color: ${(props) => (props.theme.text)};
15 | background-color: ${(props) => (props.theme.primary)};
16 | }
17 | `;
18 |
19 | export const GlobalStyles = ({ darkModeEnabled, children }) => {
20 | return (
21 |
22 |
23 | {children}
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/src/design-system/Message/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Message = ({message}) =>
4 | This component is not used by the application but is in storybook.
5 | {message}
6 |
--------------------------------------------------------------------------------
/src/design-system/Message/stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Message } from ".";
3 |
4 | export default {
5 | title: "Message",
6 | component: Message,
7 | };
8 |
9 | export const Default = () => ;
10 |
--------------------------------------------------------------------------------
/src/design-system/Toggle/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const StyledButton = styled.button`
5 | background-color: ${(props) => props.theme.primary || "green"};
6 | color: ${(props) => props.theme.text || "white"};
7 | `;
8 |
9 | export const Toggle = ({ on, onToggle }) => (
10 |
11 | {" "}
12 | {on ? "Turn Off" : "Turn On"}{" "}
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/src/design-system/Toggle/stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Toggle } from ".";
3 |
4 | export default {
5 | title: "Toggle",
6 | component: Toggle,
7 | };
8 |
9 | export const Default = () => ;
10 |
--------------------------------------------------------------------------------
/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 React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./application/App";
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 | ,
13 | document.getElementById("root")
14 | );
15 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/utilities/addOne.js:
--------------------------------------------------------------------------------
1 | export const addOne = (x) => x + 1;
2 |
--------------------------------------------------------------------------------
/src/utilities/useToggle.js:
--------------------------------------------------------------------------------
1 | // from https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks
2 |
3 | import React from "react";
4 |
5 | export const useToggle = (initial = false) => {
6 | const [on, setOnState] = React.useState(initial);
7 |
8 | const toggle = () => setOnState((o) => !o);
9 | const setOn = () => setOnState(true);
10 | const setOff = () => setOnState(false);
11 |
12 | return { on, toggle, setOn, setOff };
13 | };
14 |
--------------------------------------------------------------------------------
/src/utilities/useToggle.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render, fireEvent } from "@testing-library/react";
3 |
4 | import { useToggle } from "./useToggle";
5 |
6 | it("is off by default", () => {
7 | const onMessage = "Is On";
8 | const offMessage = "Is Off";
9 |
10 | const TestComponent = () => {
11 | const { on } = useToggle();
12 |
13 | return (
14 |
15 |
{on ? onMessage : offMessage}
16 |
17 | );
18 | };
19 |
20 | const { getByText } = render();
21 | expect(getByText(offMessage)).toBeInTheDocument();
22 | });
23 |
24 | it("toggle changes on state", () => {
25 | const onMessage = "Is On";
26 | const offMessage = "Is Off";
27 | const toggleMessage = "Toggle";
28 |
29 | const TestComponent = () => {
30 | const { on, toggle } = useToggle();
31 |
32 | return (
33 |
34 |
{on ? onMessage : offMessage}
35 |
36 |
37 | );
38 | };
39 |
40 | const { getByText } = render();
41 | fireEvent.click(getByText(toggleMessage));
42 | expect(getByText(onMessage)).toBeInTheDocument();
43 | });
44 |
45 | it("setOn changes on state from off to on", () => {
46 | const onMessage = "Is On";
47 | const offMessage = "Is Off";
48 | const setOnMessage = "Set On";
49 |
50 | const TestComponent = () => {
51 | const { on, setOn } = useToggle();
52 |
53 | return (
54 |
55 |
{on ? onMessage : offMessage}
56 |
57 |
58 | );
59 | };
60 |
61 | const { getByText } = render();
62 | fireEvent.click(getByText(setOnMessage));
63 | expect(getByText(onMessage)).toBeInTheDocument();
64 | });
65 |
66 | it("supports initial on state", () => {
67 | const onMessage = "Is On";
68 | const offMessage = "Is Off";
69 |
70 | const TestComponent = () => {
71 | const { on } = useToggle(true);
72 |
73 | return (
74 |
75 |
{on ? onMessage : offMessage}
76 |
77 | );
78 | };
79 |
80 | const { getByText } = render();
81 | expect(getByText(onMessage)).toBeInTheDocument();
82 | });
83 |
84 | it("setOff changes on state from on to off", () => {
85 | const onMessage = "Is On";
86 | const offMessage = "Is Off";
87 | const setOffMessage = "Set Off";
88 |
89 | const TestComponent = () => {
90 | const { on, setOff } = useToggle(true);
91 |
92 | return (
93 |
94 |
{on ? onMessage : offMessage}
95 |
96 |
97 | );
98 | };
99 |
100 | const { getByText } = render();
101 | fireEvent.click(getByText(setOffMessage));
102 | expect(getByText(offMessage)).toBeInTheDocument();
103 | });
104 |
--------------------------------------------------------------------------------
/storyshots/index.js:
--------------------------------------------------------------------------------
1 | import initStoryshots, {renderOnly} from '@storybook/addon-storyshots';
2 |
3 | initStoryshots({test: renderOnly});
--------------------------------------------------------------------------------