Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/useStateList.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ If `stateSet` changed, became shorter than before and `currentIndex` left in shr
- **`next()`**_`: void`_ — switches state to the next one. If last element selected it will switch to the first one;
- **`setStateAt(newIndex: number)`**_`: void`_ — set the arbitrary state by index. Indexes are looped, and can be negative.
_4ex:_ if list contains 5 elements, attempt to set index 9 will bring use to the 5th element, in case of negative index it will start counting from the right, so -17 will bring us to the 4th element.
Negative multiples of the list length wrap to the first element, so `setStateAt(-5)` selects index `0` in a five-element list.
- **`setState(state: T)`**_`: void`_ — set the arbitrary state value that exists in `stateSet`. _In case new state does not exists in `stateSet` an Error will be thrown._
- **`isFirst`**_`: boolean`_ — `true` if current state is the first one.
- **`isLast`**_`: boolean`_ — `true` if current state is the last one.
2 changes: 1 addition & 1 deletion src/useStateList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function useStateList<T>(stateSet: T[] = []): UseStateListReturn<
index.current =
newIndex >= 0
? newIndex % stateSet.length
: stateSet.length + (newIndex % stateSet.length);
: (stateSet.length + (newIndex % stateSet.length)) % stateSet.length;
update();
},
setState: (state: T) => {
Expand Down
41 changes: 41 additions & 0 deletions tests/useStateList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ describe('useStateList', () => {
});

describe('setStateAt()', () => {
it('should not rerender when setting the current index', () => {
const hook = getHook();
const renders = hook.result.all.length;

act(() => hook.result.current.setStateAt(0));

expect(hook.result.current.state).toBe('a');
expect(hook.result.all).toHaveLength(renders);
});

it('should set state by it`s index in states list', () => {
const hook = getHook();

Expand Down Expand Up @@ -109,6 +119,17 @@ describe('useStateList', () => {
act(() => hook.result.current.setStateAt(-17));
expect(hook.result.current.state).toBe('b');
});

it.each([-3, -6, -9])('should wrap index %i to the first state', (index) => {
const hook = getHook();

act(() => hook.result.current.setStateAt(index));

expect(hook.result.current.currentIndex).toBe(0);
expect(hook.result.current.state).toBe('a');
expect(hook.result.current.isFirst).toBe(true);
expect(hook.result.current.isLast).toBe(false);
});
});

describe('next()', () => {
Expand Down Expand Up @@ -143,6 +164,17 @@ describe('useStateList', () => {
});

describe('prev()', () => {
it('should keep the only state selected', () => {
const hook = getHook(['only']);

act(() => hook.result.current.prev());

expect(hook.result.current.currentIndex).toBe(0);
expect(hook.result.current.state).toBe('only');
expect(hook.result.current.isFirst).toBe(true);
expect(hook.result.current.isLast).toBe(true);
});

it('on overflow should switch to last element (should be cycled)', () => {
const hook = getHook();

Expand Down Expand Up @@ -177,6 +209,15 @@ describe('useStateList', () => {
});

describe('with empty states list', () => {
it('should use an empty list by default and reject unknown states', () => {
const hook = renderHook(() => useStateList());

expect(hook.result.current.state).toBeUndefined();
expect(() => hook.result.current.setState('a')).toThrow(
"State 'a' is not a valid state (does not exist in state list)"
);
});

it('should have `undefined` state', () => {
expect(getHook([]).result.current.state).toBe(undefined);
});
Expand Down