diff --git a/.npmignore b/.npmignore index abce592..7c8b78a 100644 --- a/.npmignore +++ b/.npmignore @@ -12,4 +12,9 @@ build-storybook.log storybook-static .github rollup.config.cjs.js -rollup.config.js \ No newline at end of file +rollup.config.js +coverage +.husky +.eslintrc.cjs +.prettierrc +.cache \ No newline at end of file diff --git a/package.json b/package.json index 554e182..2262c96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rapiders/react-hooks", - "version": "1.2.2", + "version": "1.2.5", "description": "react hooks for fast development", "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", @@ -12,7 +12,7 @@ "homepage": "https://github.com/rapiders/react-hooks", "scripts": { "test": "jest", - "prepublish": "npm run build", + "prepublishOnly": "npm run build", "build": "sh build.sh", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", diff --git a/src/index.ts b/src/index.ts index 9dd1f15..ef0151c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,5 +6,16 @@ import useCarousel from './useCarousel/useCarousel'; import useScrollRatio from './useScrollRatio'; import useInterval from './useInterval/useInterval'; import useAfterMountEffect from './useAfterMountEffect/useAfterMountEffect'; +import useRadio from './useRadio/useRadio'; -export { useInput, useAnimation, useFocusAnimation, useDragIndexCarousel, useCarousel, useScrollRatio, useInterval, useAfterMountEffect }; +export { + useInput, + useAnimation, + useFocusAnimation, + useDragIndexCarousel, + useCarousel, + useScrollRatio, + useInterval, + useAfterMountEffect, + useRadio, +}; diff --git a/src/useCarousel/useCarousel.test.tsx b/src/useCarousel/useCarousel.test.tsx index 150f906..4b4527c 100644 --- a/src/useCarousel/useCarousel.test.tsx +++ b/src/useCarousel/useCarousel.test.tsx @@ -1,35 +1,110 @@ -import { renderHook, act } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import useCarousel from './useCarousel'; +import React from 'react'; + +const DATA = [1, 2, 3, 4]; -const DATA_LENGTH = 3; describe('useCarousel 기능 테스트', () => { - it('next 함수를 통해 index를 1증가시킬 수 있다.', () => { - const { result } = renderHook(() => useCarousel(DATA_LENGTH)); - expect(result.current.isStart).toBe(true); - expect(result.current.index).toBe(0); - act(() => result.current.next()); - expect(result.current.index).toBe(1); - }); + const TestComponent = () => { + const { CarouselWrapper, prev, next, index, ref } = useCarousel(DATA.length); + return ( + <> + + {DATA.map((num) => ( +
+ {num} +
+ ))} +
+ + +
{index}
+ + ); + }; + + it('Carousel Wrapper컴포넌트를 통해 prev, next 기능을 수행할 수 있다.', async () => { + render(); + const next = await screen.findByRole('next'); + const prev = await screen.findByRole('prev'); - it('next 함수 실행시 dataLength와 index가 같아지는 경우 초기 index로 이동한다.', () => { - const { result } = renderHook(() => useCarousel(DATA_LENGTH, { startIndex: DATA_LENGTH - 1, infinity: true })); - expect(result.current.index).toBe(DATA_LENGTH - 1); - expect(result.current.isEnd).toBe(true); - act(() => result.current.next()); - expect(result.current.index).toBe(0); + fireEvent.click(next); + const indexElement = await screen.findByRole('index'); + expect(indexElement.textContent).toBe('1'); + + fireEvent.click(prev); + expect(indexElement.textContent).toBe('0'); }); - it('prev 함수를 통해 index를 1감소시킬 수 있다.', () => { - const { result } = renderHook(() => useCarousel(DATA_LENGTH, { startIndex: DATA_LENGTH - 1, infinity: true })); - expect(result.current.index).toBe(DATA_LENGTH - 1); - act(() => result.current.prev()); - expect(result.current.index).toBe(DATA_LENGTH - 2); + it('index 제한을 넘어가는 경우, prev와 next함수가 index를 변화시키지 않을 수 있다.', async () => { + render(); + const next = await screen.findByRole('next'); + const prev = await screen.findByRole('prev'); + + Array.from({ length: 10 }).forEach(() => fireEvent.click(next)); + + const indexElement = await screen.findByRole('index'); + expect(indexElement.textContent).toBe('3'); + + Array.from({ length: 10 }).forEach(() => fireEvent.click(prev)); + expect(indexElement.textContent).toBe('0'); }); +}); + +describe('useCarousel(infinity) 기능 테스트', () => { + const TestComponent = () => { + const { CarouselWrapper, prev, next, index, ref } = useCarousel(DATA.length, { infinity: true }); + return ( + <> + + {DATA.map((num) => ( +
+ {num} +
+ ))} +
+ + +
{index}
+ + ); + }; + + it('index 제한을 넘어가는 경우, prev와 next함수가 index를 적절하게 변환할 수 있다.', async () => { + render(); + const next = await screen.findByRole('next'); + const prev = await screen.findByRole('prev'); + + Array.from({ length: 5 }).forEach(() => fireEvent.click(next)); + + const indexElement = await screen.findByRole('index'); + expect(indexElement.textContent).toBe('1'); - it('prev 함수 실행시, index가 0인경우, 마지막 값으로 이동한다.', () => { - const { result } = renderHook(() => useCarousel(DATA_LENGTH, { infinity: true })); - expect(result.current.index).toBe(0); - act(() => result.current.prev()); - expect(result.current.index).toBe(DATA_LENGTH - 1); + Array.from({ length: 2 }).forEach(() => fireEvent.click(prev)); + expect(indexElement.textContent).toBe('3'); }); }); diff --git a/src/useCarousel/useCarousel.tsx b/src/useCarousel/useCarousel.tsx index 467556d..c8b5f7b 100644 --- a/src/useCarousel/useCarousel.tsx +++ b/src/useCarousel/useCarousel.tsx @@ -36,10 +36,9 @@ export default function useCarousel(dataLength: number, options?: useCarouselOpt }, [index]); const getSliderWidth = () => { - if (ref.current) { - return ref.current.clientWidth; - } - return window.innerWidth; + // getSliderWidth는 상단의 useEffect에서 호출하는데, 여기서 ref.current를 확인하므로 + // ref.current가 반드시 존재한다. + return ref!.current!.clientWidth; }; const getNext = (index: number) => {