-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit ports the useCounter hook from react-use. It is not the exact same implementation, but it retains the same API. re #33 Co-authored-by: xobotyi <[email protected]>
- Loading branch information
Showing
8 changed files
with
425 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import React from 'react'; | ||
import { useCounter } from '../..'; | ||
|
||
export const Example: React.FC = () => { | ||
const [min, { inc: incMin, dec: decMin }] = useCounter(1); | ||
const [max, { inc: incMax, dec: decMax }] = useCounter(10); | ||
const [value, { inc, dec, set, reset }] = useCounter(5, max, min); | ||
|
||
return ( | ||
<div> | ||
<div> | ||
current: {value} [min: {min}; max: {max}] | ||
</div> | ||
<br /> | ||
Current value: | ||
<button onClick={() => inc()}>Increment</button> | ||
<button onClick={() => dec()}>Decrement</button> | ||
<button onClick={() => inc(5)}>Increment (+5)</button> | ||
<button onClick={() => dec(5)}>Decrement (-5)</button> | ||
<button onClick={() => set(100)}>Set 100</button> | ||
<button onClick={() => reset()}>Reset</button> | ||
<button onClick={() => reset(25)}>Reset (25)</button> | ||
<br /> | ||
<br /> | ||
Min value: | ||
<button onClick={() => incMin()}>Increment</button> | ||
<button onClick={() => decMin()}>Decrement</button> | ||
<br /> | ||
<br /> | ||
Max value: | ||
<button onClick={() => incMax()}>Increment</button> | ||
<button onClick={() => decMax()}>Decrement</button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks'; | ||
import { Example } from './example.stories'; | ||
import { ImportPath } from '../../storybookUtil/ImportPath'; | ||
|
||
<Meta title="State/useCounter" component={Example} /> | ||
|
||
# useCounter | ||
|
||
Tracks a numeric value and offers functions for manipulating it. | ||
|
||
> **_This hook provides stable API, meaning returned functions do not change between renders_** | ||
#### Example | ||
|
||
<Canvas> | ||
<Story story={Example} inline /> | ||
</Canvas> | ||
|
||
## Reference | ||
|
||
```ts | ||
export function useCounter( | ||
initialValue: IInitialState<number> = 0, | ||
max?: number, | ||
min?: number | ||
): [number, CounterActions]; | ||
``` | ||
|
||
#### Importing | ||
|
||
<ImportPath /> | ||
|
||
#### Arguments | ||
|
||
- _**initialValue**_ _`IInitialState<number>` (default 0)_ - initial value for the counter. A number | ||
or function returning a number. | ||
- _**max**_ _`number` (default `undefined`)_ - maximum value the counter is allowed to be set. | ||
- _**min**_ _`number` (default `undefined`)_ - minimum value the counter is allowed to be set. | ||
|
||
#### Return | ||
|
||
1. **counter** - The current value of the counter. | ||
|
||
2. **methods** | ||
|
||
- **get** - Returns the current value of the counter. | ||
- **inc** - Increments the counter by the given delta (by default, 1). | ||
- **dec** - Decrements the counter by the given delta (by default, 1). | ||
- **set** - Sets the counter to the given value. | ||
- **reset** - Resets the counter to its initial value. If a value is given, it | ||
is set as the new initial value and any future calls to `reset` | ||
without arguments will reset the counter to the given value. | ||
|
||
All methods respect the `max` and `min` parameters. Instead of numerical | ||
arguments, they also accept functions returning numbers. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import { act, renderHook } from '@testing-library/react-hooks/dom'; | ||
import { useCounter } from '../..'; | ||
|
||
describe('useCounter', () => { | ||
it('should be defined', () => { | ||
expect(useCounter).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useCounter()); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
|
||
it('should have default initial value of 0', () => { | ||
const { result } = renderHook(() => useCounter()); | ||
const counter = result.current[0]; | ||
expect(counter).toEqual(0); | ||
}); | ||
|
||
it('should accept custom initial value', () => { | ||
const { result } = renderHook(() => useCounter(5)); | ||
const counter = result.current[0]; | ||
expect(counter).toEqual(5); | ||
}); | ||
|
||
it('should accept function returning a number as initial value', () => { | ||
const { result } = renderHook(() => useCounter(() => 5)); | ||
const counter = result.current[0]; | ||
expect(counter).toEqual(5); | ||
}); | ||
|
||
it('should force initial value to be at least the given minimum value', () => { | ||
const { result } = renderHook(() => useCounter(0, 10, 5)); | ||
const counter = result.current[0]; | ||
expect(counter).toEqual(5); | ||
}); | ||
|
||
it('should force initial value to be at most the given maximum value', () => { | ||
const { result } = renderHook(() => useCounter(10, 5)); | ||
const counter = result.current[0]; | ||
expect(counter).toEqual(5); | ||
}); | ||
|
||
it('get returns the current counter value', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { get } = result.current[1]; | ||
|
||
act(() => { | ||
expect(get()).toEqual(result.current[0]); | ||
}); | ||
}); | ||
|
||
it('set sets the counter to any value', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { set } = result.current[1]; | ||
|
||
act(() => { | ||
set(2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(2); | ||
|
||
act(() => { | ||
set((current: number) => current + 5); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(7); | ||
|
||
act(() => { | ||
set(12); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(12); | ||
}); | ||
|
||
it('set respects min and max parameters', () => { | ||
const { result } = renderHook(() => useCounter(0, 10, 0)); | ||
const { set } = result.current[1]; | ||
|
||
act(() => { | ||
set(-2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
|
||
act(() => { | ||
set(12); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(10); | ||
}); | ||
|
||
it('inc increments the counter by 1 if no delta given', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { inc } = result.current[1]; | ||
|
||
act(() => { | ||
inc(); | ||
}); | ||
|
||
const counter = result.current[0]; | ||
expect(counter).toEqual(1); | ||
}); | ||
|
||
it('inc increments the counter by the given delta', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { inc } = result.current[1]; | ||
|
||
act(() => { | ||
inc(2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(2); | ||
|
||
act(() => { | ||
inc((current) => current + 1); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(5); | ||
}); | ||
|
||
it('inc respects min and max parameters', () => { | ||
const { result } = renderHook(() => useCounter(0, 5, 0)); | ||
const { inc } = result.current[1]; | ||
|
||
act(() => { | ||
inc(-2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
|
||
act(() => { | ||
inc(12); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(5); | ||
}); | ||
|
||
it('dec decrements the counter by 1 if no delta given', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { dec } = result.current[1]; | ||
|
||
act(() => { | ||
dec(); | ||
}); | ||
|
||
const counter = result.current[0]; | ||
expect(counter).toEqual(-1); | ||
}); | ||
|
||
it('dec decrements the counter by the given delta', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { dec } = result.current[1]; | ||
|
||
act(() => { | ||
dec(2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(-2); | ||
|
||
act(() => { | ||
dec((current) => current + 1); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(-1); | ||
}); | ||
|
||
it('dec respects min and max parameters', () => { | ||
const { result } = renderHook(() => useCounter(0, 5, 0)); | ||
const { dec } = result.current[1]; | ||
|
||
act(() => { | ||
dec(2); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
|
||
act(() => { | ||
dec(-12); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(5); | ||
}); | ||
|
||
it('reset without arguments sets the counter to its initial value', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { reset, inc } = result.current[1]; | ||
|
||
act(() => { | ||
inc(); | ||
reset(); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
}); | ||
|
||
it('reset with argument sets the counter to its new initial value', () => { | ||
const { result } = renderHook(() => useCounter(0)); | ||
const { reset, inc } = result.current[1]; | ||
|
||
act(() => { | ||
inc(); | ||
reset(5); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(5); | ||
|
||
act(() => { | ||
inc(); | ||
reset(); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
}); | ||
|
||
it('reset respects min and max parameters', () => { | ||
const { result } = renderHook(() => useCounter(0, 10, 0)); | ||
const { reset } = result.current[1]; | ||
|
||
act(() => { | ||
reset(25); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(10); | ||
|
||
act(() => { | ||
reset(-10); | ||
}); | ||
|
||
expect(result.current[0]).toEqual(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { renderHook } from '@testing-library/react-hooks/server'; | ||
import { useCounter } from '../..'; | ||
|
||
describe('useCounter', () => { | ||
it('should be defined', () => { | ||
expect(useCounter).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useCounter()); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
}); |
Oops, something went wrong.