Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ jobs:
key: v1-dependencies-{{ checksum "package.json" }}
- run:
name: Run Test
command: yarn test
command: yarn test
- run:
name: Build the app
command: yarn build
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## React features πŸ€“

[![CircleCI](https://circleci.com/gh/Will956/react-features/tree/master.svg?style=svg)](https://circleci.com/gh/Will956/react-features/tree/master)

I usually use this repo in order to test new React features.

## Features
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@
},
"eslintConfig": {
"extends": "react-app",
"plugins": ["react-hooks"],
"plugins": [
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error"
}
Expand Down Expand Up @@ -119,6 +121,7 @@
]
},
"devDependencies": {
"eslint-plugin-react-hooks": "^1.0.1"
"eslint-plugin-react-hooks": "^1.0.1",
"react-testing-library": "^5.5.3"
}
}
15 changes: 15 additions & 0 deletions src/components/Hooks/BasicHooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { useState } from 'react';

const BasicHooks = () => {
const [count, setCount] = useState(0);

return (
<>
<button id="add" onClick={() => setCount(count + 1)}>Add one more</button>
<button id="remove" onClick={() => setCount(count - 1)}>Remove one more</button>
<p>Counter: {count}</p>
</>
)
};

export default BasicHooks;
32 changes: 32 additions & 0 deletions src/components/Hooks/BasicHooks/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { cleanup, fireEvent, render } from 'react-testing-library';
import BasicHooks from './index';

afterEach(cleanup);

describe('BasicHooks', () => {
let counter;
let addBtn;
let removeBtn;

beforeEach(() => {
const { container, getByText } = render(<BasicHooks />);
counter = container.querySelector('p');
addBtn = getByText('Add one more');
removeBtn = getByText('Remove one more');
});

it('renders BasicHooks', () => {
expect(counter.textContent).toBe('Counter: 0');
});

it('update BasicHooks counter', () => {
fireEvent.click(addBtn);
expect(counter.textContent).toBe('Counter: 1');
});

it('update BasicHooks counter', () => {
fireEvent.click(removeBtn);
expect(counter.textContent).toBe('Counter: -1');
});
});
11 changes: 11 additions & 0 deletions src/components/Hooks/ContextHooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React, { useContext } from 'react';

import { ThemeContext } from '../../../containers/Playground';

const ContextHooks = () => {
const theme = useContext(ThemeContext);

return <><p>Theme is: {theme}</p></>;
};

export default ContextHooks;
13 changes: 13 additions & 0 deletions src/components/Hooks/ContextHooks/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { cleanup, render } from 'react-testing-library';
import ContextHooks from './index';
import { ThemeContext } from '../../../containers/Playground';

afterEach(cleanup);

describe('ContextHooks', () => {
it('renders EffectHooks and should have blue as theme', () => {
const { container } = render(<ThemeContext.Provider value="random color"><ContextHooks /></ThemeContext.Provider>);
expect(container.querySelector('p').textContent).toBe('Theme is: random color');
});
});
22 changes: 22 additions & 0 deletions src/components/Hooks/EffectHooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useState, useEffect } from 'react';

const EffectHooks = () => {
const [count, setCount] = useState(0);
const [status, setStatus] = useState(null);

useEffect(() => {
setStatus(`Component mounted/updated with ${count}`);
return () => console.log('[EffectHooks] Component was unmounted');
}, [count]); // only re-run if count changes πŸ€“

return (
<>
<button id="add" onClick={() => setCount(count + 1)}>Add one more</button>
<button id="remove" onClick={() => setCount(count - 1)}>Remove one more</button>
<p data-testid="counter">Counter: {count}</p>
{status && <p data-testid="status">{status}</p>}
</>
);
};

export default EffectHooks;
31 changes: 31 additions & 0 deletions src/components/Hooks/EffectHooks/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { cleanup, fireEvent, render } from 'react-testing-library';
import EffectHooks from './index';

global.console = {
log: jest.fn()
};

afterEach(cleanup);

describe('BasicHooks', () => {
it('renders EffectHooks', () => {
const { getByTestId } = render(<EffectHooks />);
expect(getByTestId('counter').textContent).toBe('Counter: 0');
expect(getByTestId('status').textContent).toBe('Component mounted/updated with 0');
});

it('update status when component did update', () => {
const { getByTestId, getByText } = render(<EffectHooks />);
const addBtn = getByText('Add one more');
fireEvent.click(addBtn);
expect(getByTestId('status').textContent).toBe('Component mounted/updated with 1');
});

it('trigger console.log when component did unmount', () => {
const { getByText } = render(<EffectHooks />);
const addBtn = getByText('Add one more');
fireEvent.click(addBtn);
expect(console.log).toHaveBeenCalledWith('[EffectHooks] Component was unmounted');
});
});
39 changes: 39 additions & 0 deletions src/components/Hooks/ReducerHooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useReducer } from 'react';

const initialState = {
count: 0,
previous: 0
};

const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
previous: state.count
};
case 'DECREMENT':
return {
count: state.count -1,
previous: state.count,
}

default:
return state;
}
};

const ReducerHooks = () => {
const [{ count, previous }, dispatch] = useReducer(reducer, initialState);

return (
<>
<button id="add" onClick={() => dispatch({ type: 'INCREMENT' })}>Add one more</button>
<button id="remove" onClick={() => dispatch({ type: 'DECREMENT' })}>Remove one more</button>
<p>Count is: {count}</p>
<p>Previous was: {previous}</p>
</>
);
};

export default ReducerHooks;
25 changes: 25 additions & 0 deletions src/components/Hooks/ReducerHooks/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { cleanup, fireEvent, render } from 'react-testing-library';
import ReducerHooks from './index';

//TODO: Add more tests (ie. test is fn called..)

afterEach(cleanup);

describe('ReducerHooks', () => {
it('renders ReducerHooks', () => {
const { container } = render(<ReducerHooks />);
expect(container.querySelectorAll('p')[0].textContent).toBe('Count is: 0');
expect(container.querySelectorAll('p')[1].textContent).toBe('Previous was: 0');
});

it('update ReducerHooks counter', () => {
const { container, getByText } = render(<ReducerHooks />);

const addBtn = getByText('Add one more');
fireEvent.click(addBtn);

expect(container.querySelectorAll('p')[0].textContent).toBe('Count is: 1');
expect(container.querySelectorAll('p')[1].textContent).toBe('Previous was: 0');
});
});
41 changes: 31 additions & 10 deletions src/containers/Playground/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import React, { Component } from 'react';
import React, { createContext } from 'react';

import BasicHooks from '../../components/Hooks/BasicHooks';
import EffectHooks from '../../components/Hooks/EffectHooks';
import ContextHooks from '../../components/Hooks/ContextHooks';
import ReducerHooks from '../../components/Hooks/ReducerHooks';

import './index.scss';

class Playground extends Component {
render() {
return (
<header className="App-header">
<p>Edit <code>src/App.js</code> and save to reload.</p>
</header>
);
}
}
export const ThemeContext = createContext();

const Playground = () => (
<ThemeContext.Provider value="blue">
<header className="app-header">
<div className="app-block">
<p>BasicHooks:</p>
<BasicHooks />
</div>
<div className="app-block">
<p>EffectHooks:</p>
<EffectHooks />
</div>
<div className="app-block">
<p>ContextHooks:</p>
<ContextHooks />
</div>
<div className="app-block">
<p>ReducerHooks:</p>
<ReducerHooks />
</div>
</header>
</ThemeContext.Provider>
);

export default Playground;
9 changes: 8 additions & 1 deletion src/containers/Playground/index.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.App-header {
.app-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
Expand All @@ -8,3 +8,10 @@
font-size: calc(10px + 2vmin);
color: white;
}

.app-block {
padding: 30px 30px;
margin: 10px 0;
text-align: center;
border: 1px solid white;
}
43 changes: 43 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,13 @@
dependencies:
regenerator-runtime "^0.12.0"

"@babel/runtime@^7.1.5", "@babel/runtime@^7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a"
integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==
dependencies:
regenerator-runtime "^0.12.0"

"@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
Expand Down Expand Up @@ -885,6 +892,11 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==

"@sheerun/mutationobserver-shim@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==

"@svgr/core@^2.4.1":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@svgr/core/-/core-2.4.1.tgz#03a407c28c4a1d84305ae95021e8eabfda8fa731"
Expand Down Expand Up @@ -3121,6 +3133,16 @@ dom-serializer@0:
domelementtype "~1.1.1"
entities "~1.1.1"

dom-testing-library@^3.13.1:
version "3.16.5"
resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.16.5.tgz#8c71f127c6b4ee48115660798040291b59dfc894"
integrity sha512-t3OaTcDdsAqtAZNeZ13KnOJmt+2HaDJqYWyf0iBRzbG6GwrNtpF0122Ygu/qkerIwcnHMX1ihwZVx/DhaLpmTw==
dependencies:
"@babel/runtime" "^7.1.5"
"@sheerun/mutationobserver-shim" "^0.3.2"
pretty-format "^24.0.0"
wait-for-expect "^1.1.0"

domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
Expand Down Expand Up @@ -7881,6 +7903,14 @@ pretty-format@^23.6.0:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"

pretty-format@^24.0.0:
version "24.0.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0.tgz#cb6599fd73ac088e37ed682f61291e4678f48591"
integrity sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==
dependencies:
ansi-regex "^4.0.0"
ansi-styles "^3.2.0"

private@^0.1.6, private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
Expand Down Expand Up @@ -8141,6 +8171,14 @@ react-error-overlay@^5.1.0:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.2.tgz#888957b884d4b25b083a82ad550f7aad96585394"
integrity sha512-7kEBKwU9R8fKnZJBRa5RSIfay4KJwnYvKB6gODGicUmDSAhQJ7Tdnll5S0RLtYrzRfMVXlqYw61rzrSpP4ThLQ==

react-testing-library@^5.5.3:
version "5.5.3"
resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-5.5.3.tgz#cfd32da95b29e475003df4f66bd96e34eb5624bd"
integrity sha512-67jMsSJHbbm9M0NWvEzjNikDAKRdxivhP6vnpa9xPg/fYh19zkE4rMsFh5YWLpyoomm+e49fg+ubcXaEBYartA==
dependencies:
"@babel/runtime" "^7.3.1"
dom-testing-library "^3.13.1"

react@^16.8.0:
version "16.8.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.8.1.tgz#ae11831f6cb2a05d58603a976afc8a558e852c4a"
Expand Down Expand Up @@ -9837,6 +9875,11 @@ w3c-xmlserializer@^1.0.1:
webidl-conversions "^4.0.2"
xml-name-validator "^3.0.0"

wait-for-expect@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.1.0.tgz#6607375c3f79d32add35cd2c87ce13f351a3d453"
integrity sha512-vQDokqxyMyknfX3luCDn16bSaRcOyH6gGuUXMIbxBLeTo6nWuEWYqMTT9a+44FmW8c2m6TRWBdNvBBjA1hwEKg==

walker@~1.0.5:
version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
Expand Down