@comicrelief/component-library 8.77.0 → 8.77.1

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comicrelief/component-library",
3
3
  "author": "Comic Relief Engineering Team",
4
- "version": "8.77.0",
4
+ "version": "8.77.1",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
7
7
  "files": [
@@ -95,7 +95,7 @@
95
95
  "react": "^18.3.1",
96
96
  "react-dom": "^18.3.1",
97
97
  "react-test-renderer": "^18.3.1",
98
- "semantic-release": "^17.4.6",
98
+ "semantic-release": "^25.0.9",
99
99
  "styled-components": "^6.1.13",
100
100
  "vite": "^5.4.10",
101
101
  "vite-plugin-dts": "^4.3.0"
package/src/App.jsx CHANGED
@@ -1,7 +1,12 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import styled from 'styled-components';
3
- import { atoms, molecules, organisms, docs } from './demos/index.jsx';
4
- import { Layout, Sidebar, SidebarTitle, SidebarItem, Preview, PreviewHeader, PreviewBox, Logo, IsolateButton } from './App.styles.js';
3
+ import {
4
+ atoms, molecules, organisms, docs
5
+ } from './demos/index';
6
+ import {
7
+ Layout, Sidebar, SidebarTitle, SidebarItem, Preview,
8
+ PreviewHeader, PreviewBox, Logo, IsolateButton
9
+ } from './App.styles';
5
10
  import crLogo from './components/Atoms/Logos/assets/cr-logo.svg';
6
11
 
7
12
  const Chevron = styled.span`
@@ -14,7 +19,7 @@ const groups = [
14
19
  { label: 'Docs', items: docs },
15
20
  { label: 'Atoms', items: atoms },
16
21
  { label: 'Molecules', items: molecules },
17
- { label: 'Organisms', items: organisms },
22
+ { label: 'Organisms', items: organisms }
18
23
  ];
19
24
 
20
25
  // unpack all items in all groups into one flay array
@@ -28,10 +33,9 @@ function itemFromHash() {
28
33
 
29
34
  export default function App() {
30
35
  const [selected, setSelected] = useState(itemFromHash);
31
- const [isIsolated, setIsIsolated] = useState(false);
32
- const [openGroups, setOpenGroups] = useState(() =>
33
- groups.reduce((acc, g) => ({ ...acc, [g.label]: false }), {})
34
- );
36
+ const [isIsolated, setIsIsolated] = useState(import.meta.env.VITE_HIDE_NAV === 'true' || false);
37
+ const [openGroups, setOpenGroups] = useState(() => groups.reduce((acc, g) => (
38
+ { ...acc, [g.label]: false }), {}));
35
39
 
36
40
  const toggleGroup = label => setOpenGroups(prev => ({ ...prev, [label]: !prev[label] }));
37
41
 
@@ -79,12 +83,14 @@ export default function App() {
79
83
  {isIsolated ? 'Show Sidebar' : 'Isolate'}
80
84
  </IsolateButton>
81
85
  </PreviewHeader>
82
- {(currentItem?.examples ?? (currentItem?.demo != null ? [currentItem.demo] : [])).map((example, idx) => (
83
- <PreviewBox key={idx} data-testid={`${selected.name}-example-${idx + 1}`}>
84
- <div data-preview={selected.name}>
85
- {example}
86
- </div>
87
- </PreviewBox>
86
+ {(currentItem?.examples ?? (currentItem?.demo != null
87
+ ? [currentItem.demo]
88
+ : [])).map((example, idx) => (
89
+ <PreviewBox key={idx} data-testid={`${selected.name}-example-${idx + 1}`}>
90
+ <div data-preview={selected.name}>
91
+ {example}
92
+ </div>
93
+ </PreviewBox>
88
94
  ))}
89
95
  </Preview>
90
96
  </Layout>
@@ -1,15 +1,14 @@
1
1
  import React, {
2
- useEffect, useState, useCallback
2
+ useEffect, useState
3
3
  } from 'react';
4
4
  import PropTypes from 'prop-types';
5
- import {
6
- CarouselProvider, Slider, Slide, ButtonBack, ButtonNext
7
- } from 'pure-react-carousel';
5
+ import { Splide, SplideSlide } from '@splidejs/react-splide';
6
+
8
7
  import formatItems from './_utils';
9
- import 'pure-react-carousel/dist/react-carousel.es.css';
8
+
10
9
  import {
11
10
  CarouselWrapper, ImageWrapper, AmountWrapper, CopyWrapper,
12
- Heading, PeopleHelpedText, Including, Container
11
+ Heading, PeopleHelpedText, Including, Container, SlideInner, AllTextWrapper
13
12
  } from './WYMDCarousel.style';
14
13
  import Text from '../../Atoms/Text/Text';
15
14
  import { breakpointValues } from '../../../theme/shared/allBreakpoints';
@@ -28,52 +27,20 @@ const WYMDCarousel = ({ data }) => {
28
27
  paddingBottom = '2rem'
29
28
  } = data;
30
29
 
31
- // Defaults to mobile config:
32
- const [isMobile, setIsMobile] = useState(true);
33
- const [visibleSlides, setVisibleSlides] = useState(1);
34
- const [totalSlides, setTotalSlides] = useState(null);
35
30
  const [theseItems, setTheseItems] = useState();
36
31
 
37
- // Custom function to let us update the carousel config dynamically
38
- const screenResize = useCallback(() => {
39
- const screenSize = typeof window !== 'undefined' ? window.innerWidth : null;
40
- const isCurrentlyMobile = window.innerWidth < breakpointValues.M;
41
-
42
- if (screenSize !== null && (isMobile !== isCurrentlyMobile)) {
43
- setIsMobile(isCurrentlyMobile);
44
- setVisibleSlides(isCurrentlyMobile ? 1 : 3);
45
- setTotalSlides(isCurrentlyMobile ? theseItems.length : theseItems.length + 2);
46
- }
47
- }, [isMobile, theseItems]);
48
-
49
32
  // Format our data BEFORE we use it in render:
50
33
  useEffect(() => {
51
34
  setTheseItems(formatItems(data));
52
35
  }, [setTheseItems, data]);
53
36
 
54
- useEffect(() => {
55
- if (window !== 'undefined' && window.innerWidth >= breakpointValues.M) {
56
- // On inital render, update carousel plugin config
57
- // to suit the non-mobile layout and functionality:
58
- setIsMobile(false);
59
- setVisibleSlides(3);
60
- }
61
-
62
- // Hook into browser's own onresize event to call our custom wrapper function:
63
- if (typeof window !== 'undefined') window.onresize = screenResize;
64
- }, [screenResize]);
65
-
66
- if (theseItems && totalSlides === null) {
67
- // Reflects our two dummy/bookend slides for non-mobile/tablet views:
68
- setTotalSlides(isMobile ? theseItems.length : theseItems.length + 2);
69
- }
70
-
71
37
  return (
72
38
  <Container
73
39
  $paddingTop={paddingTop}
74
40
  $paddingBottom={paddingBottom}
75
41
  $backgroundColour={backgroundColour}
76
42
  >
43
+
77
44
  <CarouselWrapper
78
45
  className="CarouselWrapper"
79
46
  id={thisID}
@@ -81,7 +48,6 @@ const WYMDCarousel = ({ data }) => {
81
48
  $tabletHeight={tabletHeight}
82
49
  $desktopHeight={desktopHeight}
83
50
  >
84
-
85
51
  <Heading tag="p" weight="bold">
86
52
  { headerCopy}
87
53
  </Heading>
@@ -95,40 +61,50 @@ const WYMDCarousel = ({ data }) => {
95
61
  </Including>
96
62
 
97
63
  {theseItems && (
98
- <CarouselProvider
99
- naturalSlideWidth={50}
100
- naturalSlideHeight={200}
101
- totalSlides={totalSlides}
102
- isPlaying={autoPlay}
103
- interval={5000}
104
- visibleSlides={visibleSlides}
105
- infinite
64
+ <Splide
65
+ className="wymd-carousel"
66
+ options={{
67
+ type: 'loop',
68
+ speed: 750,
69
+ rewindSpeed: 750,
70
+ interval: 5000,
71
+ arrows: true,
72
+ pagination: false,
73
+ drag: 'free',
74
+ flickPower: 50,
75
+ perMove: 1,
76
+ dragMinThreshold: { mouse: 50, touch: 50 },
77
+ updateOnMove: true,
78
+ snap: true,
79
+ autoplay: autoPlay,
80
+ focus: 'center',
81
+ trimSpace: false,
82
+ perPage: 3,
83
+ rewind: true,
84
+ breakpoints: {
85
+ [breakpointValues.M]: {
86
+ perPage: 1
87
+ }
88
+ }
89
+ }}
106
90
  >
107
- <Slider classNameAnimation="wymd-carousel">
108
-
109
- {/* Dummy slide for our desired non-mobile layout and functionality */}
110
- {isMobile === false && (
111
- <Slide index={0} key={0} />
112
- )}
113
-
114
- {Object.keys(theseItems).map((key, index) => {
115
- // Reflect that initial dummy/bookend slide shown on non-mobile/tablet views:
116
- const thisOffsetIndex = index + (isMobile ? 0 : 1);
117
-
118
- return (
119
- // Calculate the index offset accordingly to reflect the number of slides,
120
- // but use the REAL index when determining if its the last REAL slide
121
- <Slide
122
- index={thisOffsetIndex}
123
- className={index === (theseItems.length - 1) && 'last-slide'}
124
- key={thisOffsetIndex}
125
- >
126
91
 
92
+ {Object.keys(theseItems).map((key, index) => {
93
+ const safeIndex = index;
94
+
95
+ return (
96
+ <SplideSlide
97
+ className={index === (theseItems.length - 1) && 'last-slide'}
98
+ key={safeIndex}
99
+ index={safeIndex}
100
+ data-index={safeIndex}
101
+ >
102
+ <SlideInner>
127
103
  <ImageWrapper className="image-wrapper">
128
104
  <img src={theseItems[key].image.file.url} alt={theseItems[key].copy} />
129
105
  </ImageWrapper>
130
106
 
131
- <div className="all-text-wrapper">
107
+ <AllTextWrapper>
132
108
  <AmountWrapper>
133
109
  <Text tag="h1" family="Anton">
134
110
  {theseItems[key].amount}
@@ -140,26 +116,18 @@ const WYMDCarousel = ({ data }) => {
140
116
  {theseItems[key].copy}
141
117
  </Text>
142
118
  </CopyWrapper>
143
- </div>
144
119
 
145
- </Slide>
146
- );
147
- })}
120
+ </AllTextWrapper>
121
+ </SlideInner>
122
+ </SplideSlide>
123
+ );
124
+ })}
148
125
 
149
- {/* Dummy slide for our desired non-mobile layout and functionality */}
150
- {isMobile === false && (
151
- <Slide index={theseItems.length + 1} key="bookend-last" />
152
- )}
153
-
154
- </Slider>
155
- <ButtonBack>Back</ButtonBack>
156
- <ButtonNext>Next</ButtonNext>
157
- </CarouselProvider>
126
+ </Splide>
158
127
  )}
159
128
 
160
129
  </CarouselWrapper>
161
130
  </Container>
162
-
163
131
  );
164
132
  };
165
133
 
@@ -10,6 +10,14 @@ const animationSpeed = 0.75;
10
10
  const textScaleOffsetA = 45;
11
11
  const textScaleOffsetB = 5;
12
12
 
13
+ const SlideInner = styled.div`
14
+ // Don't need any custom styles, just wanted a nice clear selector amidst the chaos
15
+ `;
16
+
17
+ const AllTextWrapper = styled.div`
18
+ // And ditto.
19
+ `;
20
+
13
21
  const ImageWrapper = styled.div`
14
22
  width: 45%;
15
23
  display: block;
@@ -25,6 +33,7 @@ const ImageWrapper = styled.div`
25
33
  display: block;
26
34
  }
27
35
 
36
+ // Dashed 'connecting' line:
28
37
  &:after {
29
38
  position: absolute;
30
39
  content: '';
@@ -128,7 +137,7 @@ const CarouselWrapper = styled.div`
128
137
  border-radius: 20px;
129
138
  ${defaultBoxShadow()}
130
139
 
131
- .carousel {
140
+ .splide {
132
141
  position: relative;
133
142
  margin: 0 auto;
134
143
  padding-top: ${spacing('l')};
@@ -137,18 +146,29 @@ const CarouselWrapper = styled.div`
137
146
  padding-top: ${spacing('l')};
138
147
  }
139
148
 
140
- button.carousel__back-button,
141
- button.carousel__next-button {
149
+ // Navigation buttons:
150
+ .splide__arrows {
151
+ width: 100%;
152
+ position: absolute;
153
+ top: 0;
154
+ left: 0;
155
+ height: 100%;
156
+ width: 100%;
157
+ }
158
+
159
+ button.splide__arrow {
142
160
  position: absolute;
143
161
  left: 0;
144
162
  top: 0;
145
- width: 33% !important;
163
+ width: 33.3%;
146
164
  height: 100%;
147
- padding: 0 !important;
165
+ padding: 0;
148
166
  box-shadow: none;
149
167
  text-indent: -9999px;
150
168
  background-color: transparent;
151
- border: none;
169
+ transform: none;
170
+ border-radius: 0;
171
+ opacity: 1;
152
172
 
153
173
  &:after {
154
174
  content: "";
@@ -162,28 +182,40 @@ const CarouselWrapper = styled.div`
162
182
  rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
163
183
  }
164
184
 
185
+ &.splide__arrow--next {
186
+ left: auto;
187
+ right: 0;
188
+
189
+ &:after {
190
+ left: auto;
191
+ right: 0;
192
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0),
193
+ rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 1));
194
+ }
195
+ }
196
+
165
197
  &:hover {
166
198
  &:after {
167
199
  opacity: 0.5;
168
200
  }
169
201
  }
170
202
 
203
+ svg {
204
+ display: none;
205
+ }
206
+
171
207
  @media ${({ theme }) => theme.allBreakpoints('M')} {
172
- width: 33.3% !important;
173
208
  &:after {
174
209
  width: 100%;
175
210
  }
176
211
  }
177
212
  }
178
213
 
214
+ // Reorientate for the right-side 'next' button
179
215
  button.carousel__next-button {
180
216
  left: auto;
181
217
  right: 0;
182
218
 
183
- &:before {
184
- transform: translate(0, -50%) rotate(-90deg);
185
- }
186
-
187
219
  &:after {
188
220
  left: auto;
189
221
  right: 0;
@@ -192,154 +224,115 @@ const CarouselWrapper = styled.div`
192
224
  }
193
225
  }
194
226
 
195
- .wymd-carousel {
196
- // Override default animations
197
- transition: -webkit-transform ${animationSpeed}s;
198
- -o-transition: transform ${animationSpeed}s;
199
- transition: transform ${animationSpeed}s;
200
- -webkit-transform: ${animationSpeed}s;
201
- will-change: transform;
202
-
203
- .last-slide {
204
- .image-wrapper:after {
205
- content: none;
227
+ // Carousel:
228
+ &.wymd-carousel {
229
+ .splide__slide {
230
+
231
+ height: ${props => props.$mobileHeight}px;
232
+
233
+ // All slides
234
+ ${SlideInner} {
235
+ text-align: center;
236
+ display: inline-flex;
237
+ align-items: center;
238
+ justify-content: flex-start;
239
+ flex-direction: column;
206
240
  }
207
- }
208
241
 
209
- .carousel__slide {
210
- // 'Mobile'
211
- padding-bottom: ${props => props.$mobileHeight}px !important;
242
+ // No dashed 'connecting' line on the final slide
243
+ &.last-slide {
244
+ ${ImageWrapper} {
245
+ &:after {
246
+ content: none;
247
+ }
248
+ }
249
+ }
212
250
 
213
251
  // 'Tablet' (and up) tweaks for the 3-visible layout
214
252
  @media ${({ theme }) => theme.allBreakpoints('M')} {
215
-
216
- padding-bottom: ${props => props.$tabletHeight}px !important;
253
+ height: ${props => props.$tabletHeight}px;
217
254
 
218
255
  // All slides:
219
- .carousel__inner-slide {
220
-
221
- // All 'ImageWrappers':
222
- > div:first-child {
256
+ ${SlideInner} {
257
+ ${ImageWrapper} {
223
258
  transition: transform ${animationSpeed}s ease;
224
259
  transform: scale(0.5);
225
-
260
+
261
+ // Dashed 'connecting' line:
226
262
  &:after {
227
- transition: transform ${animationSpeed}s ease,
228
- width ${animationSpeed}s ease,
229
- right ${animationSpeed}s ease;
230
- right: calc(-300% - 6px);
231
- transform: scale(1);
232
- width: 300%;
263
+ transition: transform ${animationSpeed}s ease, width ${animationSpeed}s ease, right ${animationSpeed}s ease;
264
+ right: calc(-360%);
265
+ width: 360%;
233
266
  }
234
267
  }
235
268
 
236
- // Scale down ALL copy
237
- .all-text-wrapper {
269
+ ${AllTextWrapper} {
238
270
  transition: transform ${animationSpeed}s ease;
239
271
  transform-origin: top;
240
- transform: translateY(calc(-${textScaleOffsetA}px + ${textScaleOffsetB}%)) scale(0.5)
272
+ transform: translateY(calc(-${textScaleOffsetA}px + ${textScaleOffsetB}%)) scale(0.5);
241
273
  }
242
274
  }
243
275
 
244
- // Our 'first' slide of the three:
245
- &.carousel__slide--visible {
246
- .carousel__inner-slide {
247
- > div:first-child {
276
+ // Previous slide, with a shorter 'connecting' line to account for larger, active slide to the right:
277
+ &.is-prev {
278
+ ${SlideInner} {
279
+ ${ImageWrapper} {
248
280
  &:after {
249
281
  right: calc(-300% - 6px);
250
- transform: scale(1);
251
282
  width: 300%;
252
283
  }
253
284
  }
254
- }
285
+ }
286
+ }
255
287
 
256
- // 2nd and 3rd:
257
- + .carousel__slide--visible {
258
- .carousel__inner-slide {
259
- > div:first-child {
260
- transform: scale(1);
261
- &:after {
262
- right: calc(-222% - 6px);
263
- transform: scale(0.5);
264
- width: 300%;
265
- }
288
+ // Active, middle slide:
289
+ &.is-active {
290
+ ${SlideInner} {
291
+ ${ImageWrapper} {
292
+ transform: scale(1);
293
+
294
+ &:after {
295
+ right: calc(-222% - 6px);
296
+ transform: scale(0.5);
297
+ width: 300%;
266
298
  }
267
299
  }
268
300
 
269
- .all-text-wrapper {
270
- transform: translateY(0) scale(1);
271
- }
272
-
273
- // Resets the 3rd slide:
274
- + .carousel__slide--visible {
275
- > div > div:first-child {
276
- transform: scale(0.5);
277
- &:after {
278
- right: calc(-300% - 6px);
279
- transform: scale(1);
280
- width: 300%;
281
- }
282
- }
283
- .all-text-wrapper {
284
- transform: translateY(calc(-${textScaleOffsetA}px + ${textScaleOffsetB}%)) scale(0.5)
285
- }
301
+ ${AllTextWrapper} {
302
+ transform: scale(1);
286
303
  }
287
- }
304
+ }
288
305
  }
289
- }
306
+ }
290
307
 
291
- // 'Desktop'
292
308
  @media ${({ theme }) => theme.allBreakpoints('L')} {
293
- padding-bottom: ${props => props.$desktopHeight}px !important;
309
+ height: ${props => props.$desktopHeight}px;
294
310
 
295
- // First
296
- &.carousel__slide--visible {
297
- .carousel__inner-slide {
298
- > div:first-child {
299
- &:after {
300
- right: calc(-250% - 6px);
301
- width: 250%;
302
- }
303
- }
311
+ ${SlideInner} ${ImageWrapper} {
312
+ &:after {
313
+ width: 315%;
314
+ right: calc(-315%);
304
315
  }
316
+ }
317
+
305
318
 
306
- // 2nd and 3rd
307
- + .carousel__slide--visible {
308
- .carousel__inner-slide {
309
- > div:first-child {
310
- &:after {
311
- right: calc(-187% - 6px);
312
- width: 250%;
313
- }
314
- }
319
+ &.is-prev {
320
+ ${SlideInner} ${ImageWrapper} {
321
+ &:after {
322
+ right: calc(-260%);
323
+ width: 260%;
315
324
  }
325
+ }
326
+ }
316
327
 
317
-
318
- // 3rd only
319
- + .carousel__slide--visible {
320
- .carousel__inner-slide {
321
- > div:first-child {
322
- &:after {
323
- right: calc(-250% - 6px);
324
- width: 250%;
325
- }
326
- }
327
- }
328
+ &.is-active {
329
+ ${SlideInner} ${ImageWrapper} {
330
+ &:after {
331
+ width: 250%;
332
+ right: calc(-192%);
328
333
  }
329
- }
330
-
331
-
334
+ }
332
335
  }
333
-
334
- }
335
- // END OF DESKTOP
336
-
337
- .carousel__inner-slide {
338
- text-align: center;
339
- display: inline-flex;
340
- align-items: center;
341
- justify-content: flex-start;
342
- flex-direction: column;
343
336
  }
344
337
  }
345
338
  }
@@ -353,5 +346,5 @@ const Container = styled.div`
353
346
 
354
347
  export {
355
348
  CarouselWrapper, ImageWrapper, AmountWrapper, CopyWrapper,
356
- Heading, PeopleHelpedText, Including, Container
349
+ Heading, PeopleHelpedText, Including, Container, SlideInner, AllTextWrapper
357
350
  };
@@ -1,37 +1,40 @@
1
1
  import React from 'react';
2
2
  import WYMDCarousel from './WYMDCarousel';
3
- import { carouselItemsComplete, carouselItemsCompleteWithPadding, carouselItemsIncomplete, carouselItemsMinimal } from '../../../data/data';
3
+ import {
4
+ carouselItemsComplete, carouselItemsCompleteWithPadding, carouselItemsIncomplete, carouselItemsMinimal
5
+ } from '../../../data/data';
4
6
  import { ExampleContainer } from '../../../demos/SharedStyles';
5
7
 
6
8
  export default function WYMDCarouselExample() {
7
9
  return (
8
10
  <>
9
11
  <ExampleContainer>
10
- <h2 style={{textAlign: 'center'}}>
11
- All fields supplied, autoplay on, default padding:
12
+ <h2 style={{ textAlign: 'center' }}>
13
+ All fields supplied, autoplay on, default padding:
12
14
  </h2>
13
- <WYMDCarousel data={carouselItemsComplete}/>
15
+ <WYMDCarousel data={carouselItemsComplete} />
14
16
  </ExampleContainer>
15
17
 
16
18
  <ExampleContainer>
17
- <h2 style={{textAlign: 'center'}}>
18
- All fields supplied, autoplay on, custom padding:
19
+ <h2 style={{ textAlign: 'center' }}>
20
+ All fields supplied, autoplay on, custom padding:
19
21
  </h2>
20
- <WYMDCarousel data={carouselItemsCompleteWithPadding}/>
22
+ <WYMDCarousel data={carouselItemsCompleteWithPadding} />
21
23
  </ExampleContainer>
22
24
 
23
25
  <ExampleContainer>
24
- <h2 style={{textAlign: 'center'}}>
25
- All REQUIRED fields supplied (nodes 1-4), various incomplete nodes, node 11 complete, autoplay off:
26
+ <h2 style={{ textAlign: 'center' }}>
27
+ All REQUIRED fields supplied (nodes 1-4), various incomplete nodes,
28
+ node 11 complete, autoplay off:
26
29
  </h2>
27
- <WYMDCarousel data={carouselItemsIncomplete}/>
30
+ <WYMDCarousel data={carouselItemsIncomplete} />
28
31
  </ExampleContainer>
29
32
 
30
33
  <ExampleContainer>
31
- <h2 style={{textAlign: 'center'}}>
32
- All REQUIRED fields supplied (nodes 1-4), autoplay off:
34
+ <h2 style={{ textAlign: 'center' }}>
35
+ All REQUIRED fields supplied (nodes 1-4), autoplay off:
33
36
  </h2>
34
- <WYMDCarousel data={carouselItemsMinimal}/>
37
+ <WYMDCarousel data={carouselItemsMinimal} />
35
38
  </ExampleContainer>
36
39
  </>
37
40
  );