@times-components/ts-components 1.145.1-76ee0965069e2a17bc1f8dcf02d24e8fefd6869a.0 → 1.145.1-7e7a12feaf05c514789e802bf49cadca92e6a2b9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/components/carousel-component/CarouselComponent.stories.js +146 -0
  2. package/dist/components/carousel-component/CarouselItem.d.ts +3 -0
  3. package/dist/components/carousel-component/CarouselItem.js +11 -0
  4. package/dist/components/carousel-component/DefaultNavigationArrow.d.ts +8 -0
  5. package/dist/components/carousel-component/DefaultNavigationArrow.js +6 -0
  6. package/dist/components/carousel-component/DefaultPageDot.d.ts +8 -0
  7. package/dist/components/carousel-component/DefaultPageDot.js +4 -0
  8. package/dist/components/{opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.d.ts → carousel-component/__tests__/CarouselComponent.test.d.ts} +0 -1
  9. package/dist/components/carousel-component/__tests__/CarouselComponent.test.js +163 -0
  10. package/dist/components/carousel-component/__tests__/CarouselItem.test.d.ts +1 -0
  11. package/dist/components/carousel-component/__tests__/CarouselItem.test.js +80 -0
  12. package/dist/components/carousel-component/__tests__/DefaultNavigationArrow.test.d.ts +1 -0
  13. package/dist/components/carousel-component/__tests__/DefaultNavigationArrow.test.js +62 -0
  14. package/dist/components/carousel-component/__tests__/DefaultPageDot.test.d.ts +1 -0
  15. package/dist/components/carousel-component/__tests__/DefaultPageDot.test.js +68 -0
  16. package/dist/components/carousel-component/hooks/__tests__/useCarousel.test.d.ts +1 -0
  17. package/dist/components/carousel-component/hooks/__tests__/useCarousel.test.js +459 -0
  18. package/dist/components/carousel-component/hooks/useCarousel.d.ts +2 -0
  19. package/dist/components/carousel-component/hooks/useCarousel.js +175 -0
  20. package/dist/components/carousel-component/index.d.ts +4 -0
  21. package/dist/components/carousel-component/index.js +20 -0
  22. package/dist/components/carousel-component/styles.d.ts +27 -0
  23. package/dist/components/carousel-component/styles.js +169 -0
  24. package/dist/components/carousel-component/types.d.ts +53 -0
  25. package/dist/components/carousel-component/types.js +2 -0
  26. package/dist/components/opta/football/opta-match-stats/shared/styles.js +1 -8
  27. package/dist/components/trip-cards/SkeletonCard.d.ts +6 -0
  28. package/dist/components/trip-cards/SkeletonCard.js +21 -0
  29. package/dist/components/trip-cards/TripCard.d.ts +3 -0
  30. package/dist/components/trip-cards/TripCard.js +49 -0
  31. package/dist/components/trip-cards/TripCards.stories.d.ts +1 -0
  32. package/dist/components/trip-cards/TripCards.stories.js +189 -0
  33. package/dist/components/trip-cards/TripCardsLayout.d.ts +3 -0
  34. package/dist/components/trip-cards/TripCardsLayout.js +37 -0
  35. package/dist/components/trip-cards/__tests__/SkeletonCard.test.d.ts +1 -0
  36. package/dist/components/trip-cards/__tests__/SkeletonCard.test.js +139 -0
  37. package/dist/components/trip-cards/__tests__/TripCard.test.d.ts +1 -0
  38. package/dist/components/trip-cards/__tests__/TripCard.test.js +95 -0
  39. package/dist/components/trip-cards/__tests__/TripCardsLayout.test.d.ts +1 -0
  40. package/dist/components/trip-cards/__tests__/TripCardsLayout.test.js +277 -0
  41. package/dist/components/trip-cards/__tests__/assets.test.d.ts +1 -0
  42. package/dist/components/trip-cards/__tests__/assets.test.js +165 -0
  43. package/dist/components/trip-cards/__tests__/helpers.test.d.ts +1 -0
  44. package/dist/components/trip-cards/__tests__/helpers.test.js +216 -0
  45. package/dist/components/trip-cards/__tests__/index.test.d.ts +1 -0
  46. package/dist/components/trip-cards/__tests__/index.test.js +433 -0
  47. package/dist/components/trip-cards/__tests__/mockData.test.d.ts +1 -0
  48. package/dist/components/trip-cards/__tests__/mockData.test.js +57 -0
  49. package/dist/components/trip-cards/__tests__/skeletonStyles.test.d.ts +1 -0
  50. package/dist/components/trip-cards/__tests__/skeletonStyles.test.js +194 -0
  51. package/dist/components/trip-cards/assets/BoatIcon.d.ts +1 -0
  52. package/dist/components/trip-cards/assets/BoatIcon.js +4 -0
  53. package/dist/components/trip-cards/assets/CalendarIcon.d.ts +1 -0
  54. package/dist/components/trip-cards/assets/CalendarIcon.js +4 -0
  55. package/dist/components/trip-cards/assets/ChevronRightIcon.d.ts +1 -0
  56. package/dist/components/trip-cards/assets/ChevronRightIcon.js +4 -0
  57. package/dist/components/trip-cards/assets/LocationIcon.d.ts +1 -0
  58. package/dist/components/trip-cards/assets/LocationIcon.js +4 -0
  59. package/dist/components/trip-cards/assets/MoonIcon.d.ts +1 -0
  60. package/dist/components/trip-cards/assets/MoonIcon.js +4 -0
  61. package/dist/components/trip-cards/assets/index.d.ts +6 -0
  62. package/dist/components/trip-cards/assets/index.js +7 -0
  63. package/dist/components/trip-cards/helpers.d.ts +4 -0
  64. package/dist/components/trip-cards/helpers.js +115 -0
  65. package/dist/components/trip-cards/index.d.ts +4 -0
  66. package/dist/components/trip-cards/index.js +70 -0
  67. package/dist/components/trip-cards/mockData.d.ts +3 -0
  68. package/dist/components/trip-cards/mockData.js +317 -0
  69. package/dist/components/trip-cards/skeletonStyles.d.ts +9 -0
  70. package/dist/components/trip-cards/skeletonStyles.js +37 -0
  71. package/dist/components/trip-cards/styles.d.ts +38 -0
  72. package/dist/components/trip-cards/styles.js +401 -0
  73. package/dist/components/trip-cards/types.d.ts +119 -0
  74. package/dist/components/trip-cards/types.js +2 -0
  75. package/dist/index.d.ts +1 -0
  76. package/dist/index.js +2 -4
  77. package/package.json +3 -3
  78. package/rnw.js +1 -1
  79. package/src/components/carousel-component/CarouselComponent.stories.tsx +220 -0
  80. package/src/components/carousel-component/CarouselItem.tsx +25 -0
  81. package/src/components/carousel-component/DefaultNavigationArrow.tsx +37 -0
  82. package/src/components/carousel-component/DefaultPageDot.tsx +20 -0
  83. package/src/components/carousel-component/__tests__/CarouselComponent.test.tsx +259 -0
  84. package/src/components/carousel-component/__tests__/CarouselItem.test.tsx +140 -0
  85. package/src/components/carousel-component/__tests__/DefaultNavigationArrow.test.tsx +153 -0
  86. package/src/components/carousel-component/__tests__/DefaultPageDot.test.tsx +105 -0
  87. package/src/components/carousel-component/hooks/__tests__/useCarousel.test.ts +625 -0
  88. package/src/components/carousel-component/hooks/useCarousel.ts +231 -0
  89. package/src/components/carousel-component/index.tsx +92 -0
  90. package/src/components/carousel-component/styles.ts +185 -0
  91. package/src/components/carousel-component/types.ts +62 -0
  92. package/src/components/opta/football/opta-match-stats/commentary/__tests__/__snapshots__/OptaMatchStatsCommentary.test.tsx.snap +1 -1
  93. package/src/components/opta/football/opta-match-stats/shared/styles.ts +0 -8
  94. package/src/components/opta/football/opta-match-stats/stats-graphs/__tests__/__snapshots__/OptaMatchStatsGraphs.test.tsx.snap +1 -1
  95. package/src/components/trip-cards/SkeletonCard.tsx +62 -0
  96. package/src/components/trip-cards/TripCard.tsx +143 -0
  97. package/src/components/trip-cards/TripCards.stories.tsx +254 -0
  98. package/src/components/trip-cards/TripCardsLayout.tsx +108 -0
  99. package/src/components/trip-cards/__tests__/SkeletonCard.test.tsx +169 -0
  100. package/src/components/trip-cards/__tests__/TripCard.test.tsx +120 -0
  101. package/src/components/trip-cards/__tests__/TripCardsLayout.test.tsx +532 -0
  102. package/src/components/trip-cards/__tests__/assets.test.tsx +206 -0
  103. package/src/components/trip-cards/__tests__/helpers.test.ts +266 -0
  104. package/src/components/trip-cards/__tests__/index.test.tsx +495 -0
  105. package/src/components/trip-cards/__tests__/mockData.test.ts +67 -0
  106. package/src/components/trip-cards/__tests__/skeletonStyles.test.tsx +256 -0
  107. package/src/components/trip-cards/assets/BoatIcon.tsx +17 -0
  108. package/src/components/trip-cards/assets/CalendarIcon.tsx +17 -0
  109. package/src/components/trip-cards/assets/ChevronRightIcon.tsx +20 -0
  110. package/src/components/trip-cards/assets/LocationIcon.tsx +17 -0
  111. package/src/components/trip-cards/assets/MoonIcon.tsx +17 -0
  112. package/src/components/trip-cards/assets/index.ts +7 -0
  113. package/src/components/trip-cards/helpers.ts +150 -0
  114. package/src/components/trip-cards/index.tsx +119 -0
  115. package/src/components/trip-cards/mockData.ts +345 -0
  116. package/src/components/trip-cards/skeletonStyles.ts +46 -0
  117. package/src/components/trip-cards/styles.ts +446 -0
  118. package/src/components/trip-cards/types.ts +128 -0
  119. package/src/index.ts +2 -3
  120. package/dist/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.d.ts +0 -10
  121. package/dist/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.js +0 -69
  122. package/dist/components/opta/football/opta-match-stats/matchday-live/MobileWidget.d.ts +0 -12
  123. package/dist/components/opta/football/opta-match-stats/matchday-live/MobileWidget.js +0 -90
  124. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.d.ts +0 -12
  125. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.js +0 -10
  126. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.js +0 -24
  127. package/dist/components/opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.js +0 -48
  128. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.d.ts +0 -1
  129. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.js +0 -19
  130. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.d.ts +0 -12
  131. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.js +0 -67
  132. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.d.ts +0 -6
  133. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.js +0 -714
  134. package/src/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.tsx +0 -108
  135. package/src/components/opta/football/opta-match-stats/matchday-live/MobileWidget.tsx +0 -158
  136. package/src/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.tsx +0 -38
  137. package/src/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.tsx +0 -23
  138. package/src/components/opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.tsx +0 -61
  139. package/src/components/opta/football/opta-match-stats/matchday-live/__tests__/__snapshots__/OptaMatchStatsMatchdayLive.test.tsx.snap +0 -61
  140. package/src/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.tsx +0 -19
  141. package/src/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.tsx +0 -81
  142. package/src/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.tsx +0 -745
  143. /package/dist/components/{opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.d.ts → carousel-component/CarouselComponent.stories.d.ts} +0 -0
@@ -0,0 +1,625 @@
1
+ import React from 'react';
2
+ import { renderHook, act } from '@testing-library/react-hooks';
3
+ import { useCarousel } from '../useCarousel';
4
+
5
+ describe('useCarousel', () => {
6
+ beforeEach(() => {
7
+ jest.useFakeTimers();
8
+ });
9
+
10
+ afterEach(() => {
11
+ jest.runOnlyPendingTimers();
12
+ jest.useRealTimers();
13
+ });
14
+
15
+ it('initializes with correct default values', () => {
16
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
17
+
18
+ expect(result.current.currentPage).toBe(0);
19
+ expect(result.current.totalPages).toBe(5);
20
+ expect(result.current.isScrolling).toBe(false);
21
+ });
22
+
23
+ it('calculates correct totalPages', () => {
24
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 3 }));
25
+
26
+ expect(result.current.totalPages).toBe(4);
27
+ });
28
+
29
+ it('provides navigation functions', () => {
30
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
31
+
32
+ expect(typeof result.current.handleNext).toBe('function');
33
+ expect(typeof result.current.handlePrevious).toBe('function');
34
+ expect(typeof result.current.scrollToPage).toBe('function');
35
+ });
36
+
37
+ it('provides mouse event handlers for drag functionality', () => {
38
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
39
+
40
+ expect(typeof result.current.handleMouseDown).toBe('function');
41
+ expect(typeof result.current.handleMouseUp).toBe('function');
42
+ expect(typeof result.current.handleMouseLeave).toBe('function');
43
+ });
44
+
45
+ it('handles single page correctly', () => {
46
+ const { result } = renderHook(() => useCarousel(2, { itemsPerPage: 2 }));
47
+
48
+ expect(result.current.totalPages).toBe(1);
49
+
50
+ act(() => {
51
+ result.current.handleNext();
52
+ });
53
+
54
+ expect(result.current.currentPage).toBe(0);
55
+ });
56
+
57
+ it('handles empty items', () => {
58
+ const { result } = renderHook(() => useCarousel(0, { itemsPerPage: 2 }));
59
+
60
+ expect(result.current.totalPages).toBe(0);
61
+ expect(result.current.currentPage).toBe(0);
62
+ });
63
+
64
+ it('updates totalPages when totalItems changes', () => {
65
+ const { result, rerender } = renderHook(
66
+ ({ total }) => useCarousel(total, { itemsPerPage: 2 }),
67
+ { initialProps: { total: 10 } }
68
+ );
69
+
70
+ expect(result.current.totalPages).toBe(5);
71
+
72
+ rerender({ total: 6 });
73
+
74
+ expect(result.current.totalPages).toBe(3);
75
+ });
76
+
77
+ it('cleans up timeout on unmount', () => {
78
+ const { unmount } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
79
+
80
+ unmount();
81
+
82
+ expect(jest.getTimerCount()).toBe(0);
83
+ });
84
+
85
+ it('defaults to itemsPerPage of 2 when not provided', () => {
86
+ const { result } = renderHook(() => useCarousel(10, {}));
87
+
88
+ expect(result.current.totalPages).toBe(5);
89
+ });
90
+
91
+ it('handles itemsPerPage of 1', () => {
92
+ const { result } = renderHook(() => useCarousel(5, { itemsPerPage: 1 }));
93
+
94
+ expect(result.current.totalPages).toBe(5);
95
+ });
96
+
97
+ it('handles large number of items', () => {
98
+ const { result } = renderHook(() =>
99
+ useCarousel(1000, { itemsPerPage: 10 })
100
+ );
101
+
102
+ expect(result.current.totalPages).toBe(100);
103
+ });
104
+
105
+ it('handles odd number of items with even itemsPerPage', () => {
106
+ const { result } = renderHook(() => useCarousel(5, { itemsPerPage: 2 }));
107
+
108
+ expect(result.current.totalPages).toBe(3);
109
+ });
110
+
111
+ it('provides carouselRef', () => {
112
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
113
+
114
+ expect(result.current.carouselRef).toBeDefined();
115
+ expect(typeof result.current.carouselRef).toBe('object');
116
+ });
117
+
118
+ it('updates when itemsPerPage option changes', () => {
119
+ const { result, rerender } = renderHook(
120
+ ({ itemsPerPage }) => useCarousel(10, { itemsPerPage }),
121
+ { initialProps: { itemsPerPage: 2 } }
122
+ );
123
+
124
+ expect(result.current.totalPages).toBe(5);
125
+
126
+ rerender({ itemsPerPage: 5 });
127
+
128
+ expect(result.current.totalPages).toBe(2);
129
+ });
130
+
131
+ it('handles itemsPerPage greater than total items', () => {
132
+ const { result } = renderHook(() => useCarousel(3, { itemsPerPage: 10 }));
133
+
134
+ expect(result.current.totalPages).toBe(1);
135
+ });
136
+
137
+ it('calls onPageChange when provided in options', () => {
138
+ const onPageChange = jest.fn();
139
+ const { result } = renderHook(() =>
140
+ useCarousel(10, { itemsPerPage: 2, onPageChange })
141
+ );
142
+
143
+ expect(result.current.currentPage).toBe(0);
144
+ expect(onPageChange).not.toHaveBeenCalled();
145
+ });
146
+
147
+ it('handles mouseDown without carouselRef', () => {
148
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
149
+
150
+ const mockEvent = ({
151
+ preventDefault: jest.fn(),
152
+ pageX: 100
153
+ } as unknown) as React.MouseEvent;
154
+
155
+ expect(result.current.carouselRef.current).toBeNull();
156
+ act(() => {
157
+ result.current.handleMouseDown(mockEvent);
158
+ });
159
+
160
+ expect(mockEvent.preventDefault).not.toHaveBeenCalled();
161
+ });
162
+
163
+ it('calls onPageChange when scrolling to a page', () => {
164
+ const onPageChange = jest.fn();
165
+ const { result } = renderHook(() =>
166
+ useCarousel(10, { itemsPerPage: 2, onPageChange })
167
+ );
168
+
169
+ const mockCard1: Partial<HTMLElement> = { offsetLeft: 0 };
170
+ const mockCard2: Partial<HTMLElement> = { offsetLeft: 400 };
171
+ const mockChildren = [
172
+ mockCard1,
173
+ mockCard2,
174
+ mockCard1,
175
+ mockCard2,
176
+ mockCard1
177
+ ];
178
+
179
+ const mockCarousel = {
180
+ scrollLeft: 0,
181
+ children: mockChildren,
182
+ scrollTo: jest.fn(),
183
+ addEventListener: jest.fn(),
184
+ removeEventListener: jest.fn()
185
+ };
186
+
187
+ (result.current.carouselRef as any).current = mockCarousel;
188
+
189
+ act(() => {
190
+ result.current.scrollToPage(1);
191
+ jest.runAllTimers();
192
+ });
193
+ expect(onPageChange).toHaveBeenCalledWith(1);
194
+ expect(mockCarousel.scrollTo).toHaveBeenCalled();
195
+ });
196
+
197
+ it('returns undefined cleanup when carousel is not mounted', () => {
198
+ const { result, unmount } = renderHook(() =>
199
+ useCarousel(10, { itemsPerPage: 2 })
200
+ );
201
+
202
+ expect(result.current.carouselRef.current).toBeNull();
203
+
204
+ unmount();
205
+ });
206
+
207
+ it('handles handleMouseUp when not dragging', () => {
208
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
209
+
210
+ const mockEvent = ({
211
+ pageX: 100
212
+ } as unknown) as React.MouseEvent;
213
+
214
+ act(() => {
215
+ result.current.handleMouseUp(mockEvent);
216
+ });
217
+
218
+ expect(result.current.currentPage).toBe(0);
219
+ });
220
+
221
+ it('handles handleMouseLeave when not dragging', () => {
222
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
223
+
224
+ const mockEvent = ({
225
+ pageX: 100
226
+ } as unknown) as React.MouseEvent;
227
+
228
+ act(() => {
229
+ result.current.handleMouseLeave(mockEvent);
230
+ });
231
+
232
+ expect(result.current.currentPage).toBe(0);
233
+ });
234
+
235
+ it('handles complete mouse drag interaction', () => {
236
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
237
+
238
+ const mockCarousel = {
239
+ scrollLeft: 0,
240
+ children: [],
241
+ scrollTo: jest.fn(),
242
+ style: { cursor: 'grab' },
243
+ addEventListener: jest.fn(),
244
+ removeEventListener: jest.fn()
245
+ };
246
+
247
+ (result.current.carouselRef as any).current = mockCarousel;
248
+
249
+ const mouseDownEvent = ({
250
+ preventDefault: jest.fn(),
251
+ pageX: 200
252
+ } as unknown) as React.MouseEvent;
253
+
254
+ const mouseUpEvent = ({
255
+ pageX: 100
256
+ } as unknown) as React.MouseEvent;
257
+
258
+ act(() => {
259
+ result.current.handleMouseDown(mouseDownEvent);
260
+ });
261
+
262
+ expect(mouseDownEvent.preventDefault).toHaveBeenCalled();
263
+ expect(mockCarousel.style.cursor).toBe('grabbing');
264
+
265
+ act(() => {
266
+ result.current.handleMouseUp(mouseUpEvent);
267
+ });
268
+
269
+ expect(mockCarousel.style.cursor).toBe('grab');
270
+ });
271
+
272
+ it('handles mouseUp with small drag distance', () => {
273
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
274
+
275
+ const mockCarousel = {
276
+ scrollLeft: 0,
277
+ children: [],
278
+ scrollTo: jest.fn(),
279
+ style: { cursor: 'grab' },
280
+ addEventListener: jest.fn(),
281
+ removeEventListener: jest.fn()
282
+ };
283
+
284
+ (result.current.carouselRef as any).current = mockCarousel;
285
+
286
+ act(() => {
287
+ result.current.handleMouseDown(({
288
+ preventDefault: jest.fn(),
289
+ pageX: 100
290
+ } as unknown) as React.MouseEvent);
291
+ });
292
+
293
+ act(() => {
294
+ result.current.handleMouseUp(({
295
+ pageX: 90
296
+ } as unknown) as React.MouseEvent);
297
+ });
298
+
299
+ expect(result.current.currentPage).toBe(0);
300
+ });
301
+
302
+ it('handles mouseLeave during active drag', () => {
303
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
304
+
305
+ const mockCarousel = {
306
+ scrollLeft: 0,
307
+ children: [],
308
+ scrollTo: jest.fn(),
309
+ style: { cursor: 'grab' },
310
+ addEventListener: jest.fn(),
311
+ removeEventListener: jest.fn()
312
+ };
313
+
314
+ (result.current.carouselRef as any).current = mockCarousel;
315
+
316
+ act(() => {
317
+ result.current.handleMouseDown(({
318
+ preventDefault: jest.fn(),
319
+ pageX: 100
320
+ } as unknown) as React.MouseEvent);
321
+ });
322
+
323
+ expect(mockCarousel.style.cursor).toBe('grabbing');
324
+
325
+ const mouseLeaveEvent = ({
326
+ pageX: 120
327
+ } as unknown) as React.MouseEvent;
328
+
329
+ act(() => {
330
+ result.current.handleMouseLeave(mouseLeaveEvent);
331
+ });
332
+
333
+ expect(mockCarousel.style.cursor).toBe('grab');
334
+ });
335
+
336
+ it('scrollToPage handles case with no children', () => {
337
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
338
+
339
+ const mockCarousel = {
340
+ children: [],
341
+ scrollTo: jest.fn(),
342
+ addEventListener: jest.fn(),
343
+ removeEventListener: jest.fn()
344
+ };
345
+
346
+ (result.current.carouselRef as any).current = mockCarousel;
347
+
348
+ act(() => {
349
+ result.current.scrollToPage(1);
350
+ });
351
+
352
+ expect(mockCarousel.scrollTo).not.toHaveBeenCalled();
353
+ });
354
+
355
+ it('handlePrevious and handleNext navigate correctly', () => {
356
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
357
+
358
+ const mockCards = Array.from({ length: 5 }, (_, i) => ({
359
+ offsetLeft: i * 200
360
+ })) as HTMLElement[];
361
+
362
+ const mockCarousel = {
363
+ children: mockCards,
364
+ scrollTo: jest.fn(),
365
+ addEventListener: jest.fn(),
366
+ removeEventListener: jest.fn()
367
+ };
368
+
369
+ (result.current.carouselRef as any).current = mockCarousel;
370
+
371
+ act(() => {
372
+ result.current.handleNext();
373
+ jest.runAllTimers();
374
+ });
375
+
376
+ expect(result.current.currentPage).toBe(1);
377
+
378
+ act(() => {
379
+ result.current.handlePrevious();
380
+ jest.runAllTimers();
381
+ });
382
+
383
+ expect(result.current.currentPage).toBe(0);
384
+ });
385
+
386
+ it('drag triggers navigation', () => {
387
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
388
+
389
+ const mockCards = Array.from({ length: 5 }, (_, i) => ({
390
+ offsetLeft: i * 200
391
+ })) as HTMLElement[];
392
+
393
+ const mockCarousel = {
394
+ children: mockCards,
395
+ scrollTo: jest.fn(),
396
+ style: { cursor: 'grab' },
397
+ addEventListener: jest.fn(),
398
+ removeEventListener: jest.fn()
399
+ };
400
+
401
+ (result.current.carouselRef as any).current = mockCarousel;
402
+
403
+ act(() => {
404
+ result.current.handleMouseDown(({
405
+ preventDefault: jest.fn(),
406
+ pageX: 200
407
+ } as unknown) as React.MouseEvent);
408
+ });
409
+
410
+ act(() => {
411
+ result.current.handleMouseUp(({
412
+ pageX: 100
413
+ } as unknown) as React.MouseEvent);
414
+ });
415
+
416
+ act(() => {
417
+ jest.runAllTimers();
418
+ });
419
+
420
+ expect(result.current.currentPage).toBe(1);
421
+ });
422
+
423
+ it('provides touch event handlers', () => {
424
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
425
+
426
+ expect(typeof result.current.handleTouchStart).toBe('function');
427
+ expect(typeof result.current.handleTouchMove).toBe('function');
428
+ expect(typeof result.current.handleTouchEnd).toBe('function');
429
+ });
430
+
431
+ it('handles touch swipe right to left (next)', () => {
432
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
433
+
434
+ const mockCards = Array.from({ length: 5 }, (_, i) => ({
435
+ offsetLeft: i * 200
436
+ })) as HTMLElement[];
437
+
438
+ const mockCarousel = {
439
+ children: mockCards,
440
+ scrollTo: jest.fn(),
441
+ addEventListener: jest.fn(),
442
+ removeEventListener: jest.fn()
443
+ };
444
+
445
+ (result.current.carouselRef as any).current = mockCarousel;
446
+
447
+ act(() => {
448
+ result.current.handleTouchStart(({
449
+ touches: [{ clientX: 200 }]
450
+ } as unknown) as React.TouchEvent);
451
+ });
452
+
453
+ act(() => {
454
+ result.current.handleTouchMove(({
455
+ touches: [{ clientX: 150 }]
456
+ } as unknown) as React.TouchEvent);
457
+ });
458
+
459
+ act(() => {
460
+ jest.runAllTimers();
461
+ });
462
+
463
+ expect(result.current.currentPage).toBe(1);
464
+
465
+ act(() => {
466
+ result.current.handleTouchEnd();
467
+ });
468
+ });
469
+
470
+ it('handles touch swipe left to right (previous)', () => {
471
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
472
+
473
+ const mockCards = Array.from({ length: 5 }, (_, i) => ({
474
+ offsetLeft: i * 200
475
+ })) as HTMLElement[];
476
+
477
+ const mockCarousel = {
478
+ children: mockCards,
479
+ scrollTo: jest.fn(),
480
+ addEventListener: jest.fn(),
481
+ removeEventListener: jest.fn()
482
+ };
483
+
484
+ (result.current.carouselRef as any).current = mockCarousel;
485
+
486
+ // First navigate to page 1
487
+ act(() => {
488
+ result.current.scrollToPage(1);
489
+ jest.runAllTimers();
490
+ });
491
+
492
+ expect(result.current.currentPage).toBe(1);
493
+
494
+ // Then swipe left to right (previous)
495
+ act(() => {
496
+ result.current.handleTouchStart(({
497
+ touches: [{ clientX: 100 }]
498
+ } as unknown) as React.TouchEvent);
499
+ });
500
+
501
+ act(() => {
502
+ result.current.handleTouchMove(({
503
+ touches: [{ clientX: 150 }]
504
+ } as unknown) as React.TouchEvent);
505
+ });
506
+
507
+ act(() => {
508
+ jest.runAllTimers();
509
+ });
510
+
511
+ expect(result.current.currentPage).toBe(0);
512
+
513
+ act(() => {
514
+ result.current.handleTouchEnd();
515
+ });
516
+ });
517
+
518
+ it('handles touch swipe with small distance (no navigation)', () => {
519
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
520
+
521
+ const mockCarousel = {
522
+ children: [],
523
+ scrollTo: jest.fn(),
524
+ addEventListener: jest.fn(),
525
+ removeEventListener: jest.fn()
526
+ };
527
+
528
+ (result.current.carouselRef as any).current = mockCarousel;
529
+
530
+ act(() => {
531
+ result.current.handleTouchStart(({
532
+ touches: [{ clientX: 100 }]
533
+ } as unknown) as React.TouchEvent);
534
+ });
535
+
536
+ act(() => {
537
+ result.current.handleTouchMove(({
538
+ touches: [{ clientX: 115 }]
539
+ } as unknown) as React.TouchEvent);
540
+ });
541
+
542
+ expect(result.current.currentPage).toBe(0);
543
+
544
+ act(() => {
545
+ result.current.handleTouchEnd();
546
+ });
547
+ });
548
+
549
+ it('ignores touch move when no touch start', () => {
550
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
551
+
552
+ const mockCarousel = {
553
+ children: [],
554
+ scrollTo: jest.fn(),
555
+ addEventListener: jest.fn(),
556
+ removeEventListener: jest.fn()
557
+ };
558
+
559
+ (result.current.carouselRef as any).current = mockCarousel;
560
+
561
+ act(() => {
562
+ result.current.handleTouchMove(({
563
+ touches: [{ clientX: 150 }]
564
+ } as unknown) as React.TouchEvent);
565
+ });
566
+
567
+ expect(result.current.currentPage).toBe(0);
568
+ });
569
+
570
+ it('ignores touch move when already swiping', () => {
571
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
572
+
573
+ const mockCards = Array.from({ length: 5 }, (_, i) => ({
574
+ offsetLeft: i * 200
575
+ })) as HTMLElement[];
576
+
577
+ const mockCarousel = {
578
+ children: mockCards,
579
+ scrollTo: jest.fn(),
580
+ addEventListener: jest.fn(),
581
+ removeEventListener: jest.fn()
582
+ };
583
+
584
+ (result.current.carouselRef as any).current = mockCarousel;
585
+
586
+ act(() => {
587
+ result.current.handleTouchStart(({
588
+ touches: [{ clientX: 200 }]
589
+ } as unknown) as React.TouchEvent);
590
+ });
591
+
592
+ act(() => {
593
+ result.current.handleTouchMove(({
594
+ touches: [{ clientX: 150 }]
595
+ } as unknown) as React.TouchEvent);
596
+ });
597
+
598
+ // Second touch move should be ignored
599
+ act(() => {
600
+ result.current.handleTouchMove(({
601
+ touches: [{ clientX: 100 }]
602
+ } as unknown) as React.TouchEvent);
603
+ });
604
+
605
+ act(() => {
606
+ jest.runAllTimers();
607
+ });
608
+
609
+ expect(result.current.currentPage).toBe(1);
610
+ });
611
+
612
+ it('handles touch start without carousel ref', () => {
613
+ const { result } = renderHook(() => useCarousel(10, { itemsPerPage: 2 }));
614
+
615
+ (result.current.carouselRef as any).current = null;
616
+
617
+ act(() => {
618
+ result.current.handleTouchStart(({
619
+ touches: [{ clientX: 100 }]
620
+ } as unknown) as React.TouchEvent);
621
+ });
622
+
623
+ expect(result.current.currentPage).toBe(0);
624
+ });
625
+ });