diff --git a/.changeset/eleven-doors-count.md b/.changeset/eleven-doors-count.md new file mode 100644 index 00000000..43f950cf --- /dev/null +++ b/.changeset/eleven-doors-count.md @@ -0,0 +1,5 @@ +--- +'nuka-carousel': patch +--- + +Fix screen measurement when scrollWidth is a float diff --git a/.gitignore b/.gitignore index 6076474c..e1c6b38c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ dist types yarn-error.log* package-lock.json +coverage cypress/screenshots cypress/videos diff --git a/packages/nuka/.gitignore b/packages/nuka/.gitignore deleted file mode 100644 index 1740628e..00000000 --- a/packages/nuka/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -LICENSE -README.md diff --git a/packages/nuka/jest.config.js b/packages/nuka/jest.config.js index 660ecf27..47fec553 100644 --- a/packages/nuka/jest.config.js +++ b/packages/nuka/jest.config.js @@ -1,4 +1,6 @@ module.exports = { + collectCoverage: true, + collectCoverageFrom: ['./src/**'], moduleNameMapper: { '\\.(css|less)$': '/__mocks__/styleMock.js', }, diff --git a/packages/nuka/package.json b/packages/nuka/package.json index 0344d0a9..12f1cdf0 100644 --- a/packages/nuka/package.json +++ b/packages/nuka/package.json @@ -17,7 +17,7 @@ "preversion": "pnpm run check", "test": "pnpm run test:unit", "test:ci": "pnpm run test:unit:ci", - "test:unit": "jest --testPathIgnorePatterns=\\(/es /lib\\)", + "test:unit": "jest", "test:unit:ci": "pnpm run test:unit", "test:unit:watch": "pnpm run test:unit --watchAll", "test:storybook": "test-storybook", diff --git a/packages/nuka/src/hooks/use-measurement.test.tsx b/packages/nuka/src/hooks/use-measurement.test.tsx new file mode 100644 index 00000000..25e9a63e --- /dev/null +++ b/packages/nuka/src/hooks/use-measurement.test.tsx @@ -0,0 +1,140 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { renderHook } from '@testing-library/react'; + +import { useMeasurement } from './use-measurement'; +import * as hooks from './use-resize-observer'; + +const domElement = {} as any; +jest.spyOn(hooks, 'useResizeObserver').mockImplementation(() => domElement); + +describe('useMeasurement', () => { + it('return the default values', () => { + const { result } = renderHook(() => + useMeasurement({ + element: { current: null }, + scrollDistance: 'screen', + }), + ); + + const { totalPages, scrollOffset } = result.current; + + expect(totalPages).toBe(0); + expect(scrollOffset).toEqual([]); + }); + + it('should return measurements for screen', () => { + const element = { + current: { + // this test covers a specific rounding error that can + // occur when the scrollWidth/offsetWidth returns a float + scrollWidth: 900, + offsetWidth: 500, + querySelector: () => ({ + children: [ + { offsetWidth: 200 }, + { offsetWidth: 300 }, + { offsetWidth: 400 }, + ], + }), + }, + } as any; + + const { result } = renderHook(() => + useMeasurement({ + element, + scrollDistance: 'screen', + }), + ); + + const { totalPages, scrollOffset } = result.current; + + expect(totalPages).toBe(2); + expect(scrollOffset).toEqual([0, 500]); + }); + + it('should return measurements for screen with fractional pixels', () => { + const element = { + current: { + // this test covers a specific rounding error that can + // occur when the scrollWidth/offsetWidth returns a float + scrollWidth: 1720, + offsetWidth: 573, + querySelector: () => ({ + children: [ + { offsetWidth: 573 }, + { offsetWidth: 573 }, + { offsetWidth: 573 }, + ], + }), + }, + } as any; + + const { result } = renderHook(() => + useMeasurement({ + element, + scrollDistance: 'screen', + }), + ); + + const { totalPages, scrollOffset } = result.current; + + expect(totalPages).toBe(3); + expect(scrollOffset).toEqual([0, 573, 1146]); + }); + + it('should return measurements for slide distance', () => { + const element = { + current: { + scrollWidth: 900, + offsetWidth: 500, + querySelector: () => ({ + children: [ + { offsetWidth: 200 }, + { offsetWidth: 300 }, + { offsetWidth: 400 }, + ], + }), + }, + } as any; + + const { result } = renderHook(() => + useMeasurement({ + element, + scrollDistance: 'slide', + }), + ); + + const { totalPages, scrollOffset } = result.current; + + expect(totalPages).toBe(3); + expect(scrollOffset).toEqual([0, 200, 500]); + }); + + it('should return measurements for numbered distance', () => { + const element = { + current: { + scrollWidth: 900, + offsetWidth: 500, + querySelector: () => ({ + children: [ + { offsetWidth: 200 }, + { offsetWidth: 300 }, + { offsetWidth: 400 }, + ], + }), + }, + } as any; + + const { result } = renderHook(() => + useMeasurement({ + element, + scrollDistance: 200, + }), + ); + + const { totalPages, scrollOffset } = result.current; + + expect(totalPages).toBe(3); + expect(scrollOffset).toEqual([0, 200, 400]); + }); +}); diff --git a/packages/nuka/src/hooks/use-measurement.tsx b/packages/nuka/src/hooks/use-measurement.tsx index 582ac4c8..a4ae89b3 100644 --- a/packages/nuka/src/hooks/use-measurement.tsx +++ b/packages/nuka/src/hooks/use-measurement.tsx @@ -27,7 +27,7 @@ export function useMeasurement({ element, scrollDistance }: MeasurementProps) { switch (scrollDistance) { case 'screen': { - const pageCount = Math.ceil(scrollWidth / visibleWidth); + const pageCount = Math.round(scrollWidth / visibleWidth); setTotalPages(pageCount); setScrollOffset(arraySeq(pageCount, visibleWidth)); @@ -56,7 +56,7 @@ export function useMeasurement({ element, scrollDistance }: MeasurementProps) { } default: { if (typeof scrollDistance === 'number') { - // find the number of pages required to scroll the all slides + // find the number of pages required to scroll all the slides // to the end of the container const pageCount = Math.ceil(remainder / scrollDistance) + 1;