@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,143 @@
1
+ import React, { FC, useState } from 'react';
2
+ import { Placeholder } from '@times-components/image';
3
+ import { TripCardProps } from './types';
4
+ import {
5
+ CardContainer,
6
+ ImageContainer,
7
+ CardImage,
8
+ OfferLabel,
9
+ GiftBanner,
10
+ CardContent,
11
+ TopContainer,
12
+ BottomContainer,
13
+ Headline,
14
+ DataPointsList,
15
+ DataPoint,
16
+ DataIcon,
17
+ PriceSection,
18
+ PriceContainer,
19
+ OfferContainer,
20
+ PriceLabel,
21
+ OriginalPrice,
22
+ CurrentPrice,
23
+ LogoContainer,
24
+ CTAButton,
25
+ PriceSuffix
26
+ } from './styles';
27
+ import { MoonIcon, LocationIcon, BoatIcon, CalendarIcon } from './assets';
28
+
29
+ export const TripCard: FC<TripCardProps> = ({
30
+ card,
31
+ isStaticGrid,
32
+ imgHeight = {}
33
+ }) => {
34
+ const [imageLoaded, setImageLoaded] = useState(false);
35
+
36
+ return (
37
+ <CardContainer>
38
+ <a
39
+ href={card.cta_url}
40
+ target="_blank"
41
+ rel="noopener noreferrer"
42
+ style={{ textDecoration: 'none', color: 'inherit' }}
43
+ >
44
+ <ImageContainer isStaticGrid={isStaticGrid} {...imgHeight}>
45
+ {!imageLoaded && <Placeholder />}
46
+ <CardImage
47
+ src={card.image}
48
+ alt={card.headline}
49
+ onLoad={() => setImageLoaded(true)}
50
+ style={{ display: imageLoaded ? 'block' : 'none' }}
51
+ />
52
+ {card.offer_label && <OfferLabel>{card.offer_label}</OfferLabel>}
53
+ {card.gift_banner && <GiftBanner>{card.gift_banner}</GiftBanner>}
54
+ </ImageContainer>
55
+ </a>
56
+
57
+ <CardContent>
58
+ <TopContainer>
59
+ <a
60
+ href={card.cta_url}
61
+ target="_blank"
62
+ rel="noopener noreferrer"
63
+ style={{ textDecoration: 'none', color: 'inherit' }}
64
+ >
65
+ <Headline>{card.headline}</Headline>
66
+ </a>
67
+
68
+ <DataPointsList>
69
+ {card.date && (
70
+ <DataPoint>
71
+ <DataIcon>
72
+ <CalendarIcon />
73
+ </DataIcon>
74
+ <span>{card.date}</span>
75
+ </DataPoint>
76
+ )}
77
+ {card.duration && (
78
+ <DataPoint>
79
+ <DataIcon>
80
+ <MoonIcon />
81
+ </DataIcon>
82
+ <span>{card.duration}</span>
83
+ </DataPoint>
84
+ )}
85
+ {card.route && (
86
+ <DataPoint>
87
+ <DataIcon>
88
+ <LocationIcon />
89
+ </DataIcon>
90
+ <span>{card.route}</span>
91
+ </DataPoint>
92
+ )}
93
+ {card.ship && (
94
+ <DataPoint>
95
+ <DataIcon>
96
+ <BoatIcon />
97
+ </DataIcon>
98
+ <span>{card.ship}</span>
99
+ </DataPoint>
100
+ )}
101
+ </DataPointsList>
102
+ </TopContainer>
103
+
104
+ <BottomContainer>
105
+ <PriceSection>
106
+ <PriceContainer>
107
+ <OfferContainer>
108
+ {card.price !== 'Enquire now' && <PriceLabel>From</PriceLabel>}
109
+ {card.original_price && (
110
+ <OriginalPrice>{card.original_price}</OriginalPrice>
111
+ )}
112
+ </OfferContainer>
113
+ <CurrentPrice $isEnquire={card.price === 'Enquire now'}>
114
+ {card.price}
115
+ {card.price !== 'Enquire now' && <PriceSuffix>pp</PriceSuffix>}
116
+ </CurrentPrice>
117
+ </PriceContainer>
118
+
119
+ {card.logo && (
120
+ <LogoContainer>
121
+ <a
122
+ href={card.logo_url}
123
+ target="_blank"
124
+ rel="noopener noreferrer"
125
+ >
126
+ <img src={card.logo} alt="Partner logo" />
127
+ </a>
128
+ </LogoContainer>
129
+ )}
130
+ </PriceSection>
131
+
132
+ <CTAButton
133
+ href={card.cta_url}
134
+ target="_blank"
135
+ rel="noopener noreferrer"
136
+ >
137
+ {card.cta_text || 'View Itinerary'}
138
+ </CTAButton>
139
+ </BottomContainer>
140
+ </CardContent>
141
+ </CardContainer>
142
+ );
143
+ };
@@ -0,0 +1,254 @@
1
+ import React from 'react';
2
+ import { storiesOf } from '@storybook/react';
3
+ import { TripCards } from './index';
4
+ import styled from 'styled-components';
5
+
6
+ const spacing = (multiplier: number) => `${multiplier * 8}px`;
7
+
8
+ const breakpoints = {
9
+ medium: 768,
10
+ wide: 1024
11
+ };
12
+
13
+ const InteractiveContainer = styled.div`
14
+ position: relative;
15
+ margin-bottom: ${spacing(4)};
16
+ padding: 0 ${spacing(2)};
17
+
18
+ @media (min-width: ${breakpoints.medium}px) {
19
+ width: 80.8%;
20
+ margin-left: auto;
21
+ margin-right: auto;
22
+ padding: 0;
23
+ }
24
+
25
+ @media (min-width: ${breakpoints.wide}px) {
26
+ width: 56.2%;
27
+ }
28
+ `;
29
+
30
+ storiesOf('Typescript Component/TripCards', module)
31
+ .add('Channel View Two Cards', () => (
32
+ <TripCards
33
+ element={{
34
+ class: 'trip-cards',
35
+ tripcards: btoa(JSON.stringify([2074350, 2140335])),
36
+ title: 'Similar cruises',
37
+ titleurl: 'https://www.thetimes.com/travel/holidays',
38
+ description: 'Brought to you by Times Holidays'
39
+ }}
40
+ useMockData={true}
41
+ widthContainerConfig={{
42
+ mobile: '390px',
43
+ tablet: '728px',
44
+ desktop: '984px',
45
+ xl: '1144px'
46
+ }}
47
+ />
48
+ ))
49
+ .add('Channel View Three Cards', () => (
50
+ <TripCards
51
+ element={{
52
+ class: 'trip-cards',
53
+ tripcards: btoa(JSON.stringify([2074350, 2140335, 2177685])),
54
+ title: 'Featured Voyages',
55
+ titleurl: 'https://www.thetimes.com/travel/holidays',
56
+ description: 'Handpicked by our travel experts'
57
+ }}
58
+ forceStaticGrid={true}
59
+ maxWidthItemConfig={{
60
+ mobile: '320px',
61
+ tablet: '320px',
62
+ desktop: '314px',
63
+ xl: '368px'
64
+ }}
65
+ widthContainerConfig={{
66
+ mobile: '390px',
67
+ tablet: '728px',
68
+ desktop: '984px',
69
+ xl: '1144px'
70
+ }}
71
+ imgHeight={{
72
+ $heightXs: '153px',
73
+ $heightSm: '213px',
74
+ $heightMd: '213px',
75
+ $heightLg: '210px',
76
+ $heightXl: '245px'
77
+ }}
78
+ useMockData={true}
79
+ />
80
+ ))
81
+ .add('Article View Two Trip Cards', () => (
82
+ <InteractiveContainer>
83
+ <TripCards
84
+ element={{
85
+ class: 'trip-cards',
86
+ tripcards: btoa(JSON.stringify([2074350, 2140335])),
87
+ title: 'Similar cruises',
88
+ titleurl: 'https://www.thetimes.com/travel/holidays',
89
+ description: 'Brought to you by Times Holidays'
90
+ }}
91
+ useMockData={true}
92
+ maxWidthItemConfig={{
93
+ mobile: '320px',
94
+ tablet: '260px',
95
+ desktop: '260px',
96
+ xl: '300px'
97
+ }}
98
+ widthContainerConfig={{
99
+ mobile: '390px',
100
+ tablet: '620px',
101
+ desktop: '574px',
102
+ xl: '663px'
103
+ }}
104
+ imgHeight={{
105
+ $heightXs: '153px',
106
+ $heightSm: '213px',
107
+ $heightMd: '173px',
108
+ $heightLg: '173px',
109
+ $heightXl: '200px'
110
+ }}
111
+ />
112
+ </InteractiveContainer>
113
+ ))
114
+ .add('Article View Three Cards', () => (
115
+ <InteractiveContainer>
116
+ <TripCards
117
+ element={{
118
+ class: 'trip-cards',
119
+ tripcards: btoa(JSON.stringify([2074350, 2140335, 2177685])),
120
+ title: 'Similar cruises',
121
+ titleurl: 'https://www.thetimes.com/travel/holidays',
122
+ description: 'Brought to you by Times Holidays'
123
+ }}
124
+ useMockData={true}
125
+ maxWidthItemConfig={{
126
+ mobile: '320px',
127
+ tablet: '260px',
128
+ desktop: '260px',
129
+ xl: '300px'
130
+ }}
131
+ widthContainerConfig={{
132
+ mobile: '390px',
133
+ tablet: '620px',
134
+ desktop: '574px',
135
+ xl: '663px'
136
+ }}
137
+ imgHeight={{
138
+ $heightXs: '153px',
139
+ $heightSm: '213px',
140
+ $heightMd: '213px',
141
+ $heightLg: '173px',
142
+ $heightXl: '200px'
143
+ }}
144
+ />
145
+ </InteractiveContainer>
146
+ ))
147
+ .add('Article View Two Trip Cards', () => (
148
+ <InteractiveContainer>
149
+ <TripCards
150
+ element={{
151
+ class: 'trip-cards',
152
+ tripcards: btoa(JSON.stringify([2074350, 2140335])),
153
+ title: 'Similar cruises',
154
+ titleurl: 'https://www.thetimes.com/travel/holidays',
155
+ description: 'Brought to you by Times Holidays'
156
+ }}
157
+ useMockData={true}
158
+ maxWidthItemConfig={{
159
+ mobile: '320px',
160
+ tablet: '260px',
161
+ desktop: '260px',
162
+ xl: '300px'
163
+ }}
164
+ widthContainerConfig={{
165
+ mobile: '390px',
166
+ tablet: '620px',
167
+ desktop: '574px',
168
+ xl: '663px'
169
+ }}
170
+ imgHeight={{
171
+ $heightXs: '153px',
172
+ $heightSm: '213px',
173
+ $heightMd: '173px',
174
+ $heightLg: '173px',
175
+ $heightXl: '200px'
176
+ }}
177
+ />
178
+ </InteractiveContainer>
179
+ ))
180
+ .add('Article View Trip Cards', () => (
181
+ <InteractiveContainer>
182
+ <TripCards
183
+ element={{
184
+ class: 'trip-cards',
185
+ tripcards: btoa(JSON.stringify([2074350, 2140335, 2177685])),
186
+ title: 'Similar cruises',
187
+ titleurl: 'https://www.thetimes.com/travel/holidays',
188
+ description: 'Brought to you by Times Holidays'
189
+ }}
190
+ useMockData={true}
191
+ maxWidthItemConfig={{
192
+ mobile: '320px',
193
+ tablet: '260px',
194
+ desktop: '260px',
195
+ xl: '300px'
196
+ }}
197
+ widthContainerConfig={{
198
+ mobile: '390px',
199
+ tablet: '620px',
200
+ desktop: '574px',
201
+ xl: '663px'
202
+ }}
203
+ imgHeight={{
204
+ $heightXs: '153px',
205
+ $heightSm: '213px',
206
+ $heightMd: '173px',
207
+ $heightLg: '173px',
208
+ $heightXl: '200px'
209
+ }}
210
+ />
211
+ </InteractiveContainer>
212
+ ))
213
+ .add('Article View Six Cards', () => (
214
+ <InteractiveContainer>
215
+ <TripCards
216
+ element={{
217
+ class: 'trip-cards',
218
+ tripcards: btoa(
219
+ JSON.stringify([
220
+ 2074350,
221
+ 2140335,
222
+ 2177685,
223
+ 2074351,
224
+ 2140336,
225
+ 2177686
226
+ ])
227
+ ),
228
+ title: 'Similar cruises',
229
+ titleurl: 'https://www.thetimes.com/travel/holidays',
230
+ description: 'Brought to you by Times Holidays'
231
+ }}
232
+ useMockData={true}
233
+ maxWidthItemConfig={{
234
+ mobile: '320px',
235
+ tablet: '260px',
236
+ desktop: '260px',
237
+ xl: '300px'
238
+ }}
239
+ widthContainerConfig={{
240
+ mobile: '390px',
241
+ tablet: '620px',
242
+ desktop: '574px',
243
+ xl: '663px'
244
+ }}
245
+ imgHeight={{
246
+ $heightXs: '153px',
247
+ $heightSm: '213px',
248
+ $heightMd: '173px',
249
+ $heightLg: '173px',
250
+ $heightXl: '200px'
251
+ }}
252
+ />
253
+ </InteractiveContainer>
254
+ ));
@@ -0,0 +1,108 @@
1
+ import React, { FC, useRef, useState, useEffect } from 'react';
2
+ import { CarouselComponent, CarouselItem } from '../carousel-component';
3
+ import { TripCardsLayoutProps } from './types';
4
+ import {
5
+ Container,
6
+ TitleSection,
7
+ TitleBar,
8
+ TitleContent,
9
+ Title,
10
+ Subtitle,
11
+ TitleLink,
12
+ StaticCardsGrid
13
+ } from './styles';
14
+ import { ChevronRightIcon } from './assets';
15
+
16
+ export const TripCardsLayout: FC<TripCardsLayoutProps> = ({
17
+ element,
18
+ items,
19
+ CardComponent,
20
+ itemsPerPage,
21
+ widthContainerConfig = {},
22
+ widthItemConfig,
23
+ maxWidthItemConfig,
24
+ imgHeight,
25
+ forceStaticGrid
26
+ }) => {
27
+ const { titleurl, title, description } = element;
28
+ const gridRef = useRef<HTMLDivElement>(null);
29
+ const [hasOverflow, setHasOverflow] = useState(false);
30
+ const [isTabletMobile, setIsTabletMobile] = useState(false);
31
+
32
+ const isStaticGrid = items.length === 2 || forceStaticGrid;
33
+
34
+ useEffect(
35
+ () => {
36
+ const checkScreenAndOverflow = () => {
37
+ setIsTabletMobile(window.innerWidth <= 768);
38
+
39
+ if (isStaticGrid && gridRef.current) {
40
+ const isOverflowing =
41
+ gridRef.current.scrollWidth > gridRef.current.clientWidth;
42
+ setHasOverflow(isOverflowing);
43
+ }
44
+ };
45
+
46
+ checkScreenAndOverflow();
47
+ window.addEventListener('resize', checkScreenAndOverflow);
48
+ return () => window.removeEventListener('resize', checkScreenAndOverflow);
49
+ },
50
+ [items, isStaticGrid]
51
+ );
52
+
53
+ const shouldUseCarousel =
54
+ (isTabletMobile && items.length >= 3) || (!isStaticGrid || hasOverflow);
55
+
56
+ return (
57
+ <Container data-testid="trip-cards-container" {...widthContainerConfig}>
58
+ <TitleSection data-testid="title-section">
59
+ <TitleBar data-testid="title-bar">
60
+ <TitleContent data-testid="title-content">
61
+ <Title data-testid="trip-cards-title">{title}</Title>
62
+ <Subtitle data-testid="trip-cards-subtitle">{description}</Subtitle>
63
+ </TitleContent>
64
+ {titleurl && (
65
+ <TitleLink
66
+ href={titleurl}
67
+ target="_blank"
68
+ rel="noopener noreferrer"
69
+ data-testid="title-link"
70
+ >
71
+ <ChevronRightIcon />
72
+ </TitleLink>
73
+ )}
74
+ </TitleBar>
75
+ </TitleSection>
76
+
77
+ {!shouldUseCarousel ? (
78
+ <StaticCardsGrid data-testid="static-cards-grid" ref={gridRef}>
79
+ {items.map(item => (
80
+ <CardComponent
81
+ imgHeight={imgHeight}
82
+ key={item.id}
83
+ card={item.data}
84
+ isStaticGrid={true}
85
+ />
86
+ ))}
87
+ </StaticCardsGrid>
88
+ ) : (
89
+ <CarouselComponent
90
+ items={items.map(item => (
91
+ <CarouselItem
92
+ key={item.id}
93
+ widthItemConfig={widthItemConfig}
94
+ maxWidthItemConfig={maxWidthItemConfig}
95
+ >
96
+ <CardComponent card={item.data} imgHeight={imgHeight} />
97
+ </CarouselItem>
98
+ ))}
99
+ options={{
100
+ itemsPerPage
101
+ }}
102
+ showArrows={!isStaticGrid}
103
+ showDots={true}
104
+ />
105
+ )}
106
+ </Container>
107
+ );
108
+ };
@@ -0,0 +1,169 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { SkeletonCard } from '../SkeletonCard';
5
+
6
+ describe('SkeletonCard', () => {
7
+ it('renders without crashing', () => {
8
+ const { container } = render(<SkeletonCard />);
9
+ expect(container.firstChild).toBeInTheDocument();
10
+ });
11
+
12
+ it('renders placeholder image', () => {
13
+ render(<SkeletonCard />);
14
+ const imageContainer = screen.getByTestId('skeleton-image-container');
15
+ expect(imageContainer).toBeInTheDocument();
16
+ });
17
+
18
+ it('renders skeleton headline', () => {
19
+ render(<SkeletonCard />);
20
+ const skeletonHeadline = screen.getByTestId('skeleton-headline');
21
+ expect(skeletonHeadline).toBeInTheDocument();
22
+ });
23
+
24
+ it('renders four skeleton data points in the list', () => {
25
+ render(<SkeletonCard />);
26
+ const dataPoints = [
27
+ screen.getByTestId('skeleton-data-point-1'),
28
+ screen.getByTestId('skeleton-data-point-2'),
29
+ screen.getByTestId('skeleton-data-point-3'),
30
+ screen.getByTestId('skeleton-data-point-4')
31
+ ];
32
+ expect(dataPoints.length).toBe(4);
33
+ });
34
+
35
+ it('renders skeleton lines for data points', () => {
36
+ render(<SkeletonCard />);
37
+ const skeletonLines = [
38
+ screen.getByTestId('skeleton-line-1'),
39
+ screen.getByTestId('skeleton-line-2'),
40
+ screen.getByTestId('skeleton-line-3'),
41
+ screen.getByTestId('skeleton-line-4'),
42
+ screen.getByTestId('skeleton-price-label')
43
+ ];
44
+ expect(skeletonLines.length).toBe(5);
45
+ });
46
+
47
+ it('renders skeleton price', () => {
48
+ render(<SkeletonCard />);
49
+ const skeletonPrice = screen.getByTestId('skeleton-price');
50
+ expect(skeletonPrice).toBeInTheDocument();
51
+ });
52
+
53
+ it('renders skeleton button', () => {
54
+ render(<SkeletonCard />);
55
+ const skeletonButton = screen.getByTestId('skeleton-button');
56
+ expect(skeletonButton).toBeInTheDocument();
57
+ });
58
+
59
+ it('has correct card structure', () => {
60
+ render(<SkeletonCard />);
61
+ const cardContainer = screen.getByTestId('skeleton-card-container');
62
+ const cardContent = screen.getByTestId('skeleton-card-content');
63
+ const topContainer = screen.getByTestId('skeleton-top-container');
64
+ const bottomContainer = screen.getByTestId('skeleton-bottom-container');
65
+
66
+ expect(cardContainer).toBeInTheDocument();
67
+ expect(cardContent).toBeInTheDocument();
68
+ expect(topContainer).toBeInTheDocument();
69
+ expect(bottomContainer).toBeInTheDocument();
70
+ });
71
+
72
+ it('renders price section with container', () => {
73
+ render(<SkeletonCard />);
74
+ const priceSection = screen.getByTestId('skeleton-price-section');
75
+ const priceContainer = screen.getByTestId('skeleton-price-container');
76
+
77
+ expect(priceSection).toBeInTheDocument();
78
+ expect(priceContainer).toBeInTheDocument();
79
+ });
80
+
81
+ it('renders data points list', () => {
82
+ render(<SkeletonCard />);
83
+ const dataPointsList = screen.getByTestId('skeleton-data-points-list');
84
+ expect(dataPointsList).toBeInTheDocument();
85
+ });
86
+
87
+ it('skeleton lines are rendered for each data point', () => {
88
+ render(<SkeletonCard />);
89
+ const skeletonLines = [
90
+ screen.getByTestId('skeleton-line-1'),
91
+ screen.getByTestId('skeleton-line-2'),
92
+ screen.getByTestId('skeleton-line-3'),
93
+ screen.getByTestId('skeleton-line-4'),
94
+ screen.getByTestId('skeleton-price-label')
95
+ ];
96
+ expect(skeletonLines.length).toBe(5);
97
+ });
98
+
99
+ it('renders with correct skeleton styling', () => {
100
+ render(<SkeletonCard />);
101
+
102
+ expect(screen.getByTestId('skeleton-headline')).toBeInTheDocument();
103
+ expect(screen.getByTestId('skeleton-price')).toBeInTheDocument();
104
+ expect(screen.getByTestId('skeleton-button')).toBeInTheDocument();
105
+ // Check that skeleton lines are present
106
+ expect(screen.getByTestId('skeleton-line-1')).toBeInTheDocument();
107
+ });
108
+
109
+ it('renders consistent structure', () => {
110
+ render(<SkeletonCard />);
111
+
112
+ expect(screen.getByTestId('skeleton-card-container')).toBeInTheDocument();
113
+ expect(screen.getByTestId('skeleton-image-container')).toBeInTheDocument();
114
+ expect(screen.getByTestId('skeleton-card-content')).toBeInTheDocument();
115
+ expect(screen.getByTestId('skeleton-top-container')).toBeInTheDocument();
116
+ expect(screen.getByTestId('skeleton-bottom-container')).toBeInTheDocument();
117
+ });
118
+
119
+ it('can render multiple skeleton cards', () => {
120
+ render(
121
+ <>
122
+ <SkeletonCard />
123
+ <SkeletonCard />
124
+ <SkeletonCard />
125
+ </>
126
+ );
127
+
128
+ const cards = screen.getAllByTestId('skeleton-card-container');
129
+ expect(cards.length).toBe(3);
130
+ });
131
+
132
+ it('renders skeleton elements in correct order', () => {
133
+ render(<SkeletonCard />);
134
+
135
+ const cardContent = screen.getByTestId('skeleton-card-content');
136
+ const topContainer = screen.getByTestId('skeleton-top-container');
137
+ const bottomContainer = screen.getByTestId('skeleton-bottom-container');
138
+
139
+ // Verify both containers exist as children of card content
140
+ expect(cardContent.contains(topContainer)).toBe(true);
141
+ expect(cardContent.contains(bottomContainer)).toBe(true);
142
+ });
143
+
144
+ it('skeleton button has correct structure', () => {
145
+ render(<SkeletonCard />);
146
+ const skeletonButton = screen.getByTestId('skeleton-button');
147
+
148
+ expect(skeletonButton.tagName.toLowerCase()).toBe('div');
149
+ });
150
+
151
+ it('skeleton lines have correct width props', () => {
152
+ render(<SkeletonCard />);
153
+
154
+ // Check that the skeleton lines are rendered
155
+ expect(screen.getByTestId('skeleton-line-1')).toBeInTheDocument();
156
+ expect(screen.getByTestId('skeleton-line-2')).toBeInTheDocument();
157
+ expect(screen.getByTestId('skeleton-line-3')).toBeInTheDocument();
158
+ expect(screen.getByTestId('skeleton-line-4')).toBeInTheDocument();
159
+ });
160
+
161
+ it('renders without any interactive elements', () => {
162
+ const { container } = render(<SkeletonCard />);
163
+ const buttons = container.querySelectorAll('button');
164
+ const links = container.querySelectorAll('a');
165
+
166
+ expect(buttons.length).toBe(0);
167
+ expect(links.length).toBe(0);
168
+ });
169
+ });