-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ENG-3464 - Richtext Carousel (#670)
* feat: Richtext Carousel * Style putzing * Sizing tweaks * Sorting out text renders * Mo tweaks * Config tweak * Dist experimenting * Add dist components * Add dist index * Add dist theme * Add dist utils * Ignore dist styleguide * Export stuff * Tidyup * Commit updates in build * Render properly * Render node copy properly * Provide default heights * Dynamically handle new bg colour prop * Handle more colour logic * Actually commit the built stuff * More options * Tweakz * Spacing tweak * Another tweak * Update build * Remove dist folder again * Reignore dist * Update test with correct data * Tidyup * Housekeeping * Oh no more tidyup * Whoops
- Loading branch information
1 parent
adba9ea
commit fb8e985
Showing
7 changed files
with
663 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 160 additions & 0 deletions
160
src/components/Organisms/RichtextCarousel/RichtextCarousel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import React, { | ||
useEffect, useState, useCallback | ||
} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
CarouselProvider, Slider, Slide, ButtonBack, ButtonNext | ||
} from 'pure-react-carousel'; | ||
import 'pure-react-carousel/dist/react-carousel.es.css'; | ||
import { | ||
CarouselWrapper, SlideCopyWrapper, HeadingCopyWrapper | ||
} from './RichtextCarousel.style'; | ||
import { breakpointValues } from '../../../theme/shared/allBreakpoints'; | ||
|
||
const RichtextCarousel = ({ | ||
data: { | ||
contentful_id: thisID, | ||
autoPlay, | ||
nodes, | ||
headingCopy, | ||
// Set some defaults for good measure: | ||
mobileHeight = 300, | ||
tabletHeight = 350, | ||
desktopHeight = 350, | ||
carouselBackgroundColour = 'white', | ||
nodeBackgroundColour = 'white', | ||
nodeOutlineColour = 'grey' | ||
} | ||
}) => { | ||
// Defaults to mobile config: | ||
const [isMobile, setIsMobile] = useState(true); | ||
const [visibleSlides, setVisibleSlides] = useState(1); | ||
const [totalSlides, setTotalSlides] = useState(null); | ||
const [theseItems, setTheseItems] = useState(); | ||
|
||
// Custom function to let us update the carousel config dynamically | ||
const screenResize = useCallback(() => { | ||
const screenSize = typeof window !== 'undefined' ? window.innerWidth : null; | ||
const isCurrentlyMobile = window.innerWidth < breakpointValues.M; | ||
|
||
if (screenSize !== null && (isMobile !== isCurrentlyMobile)) { | ||
setIsMobile(isCurrentlyMobile); | ||
setVisibleSlides(isCurrentlyMobile ? 1 : 3); | ||
setTotalSlides(isCurrentlyMobile ? theseItems.length : theseItems.length + 2); | ||
} | ||
}, [isMobile, theseItems]); | ||
|
||
// Cache our data source, using as a flag for render logic: | ||
useEffect(() => { | ||
setTheseItems(nodes); | ||
}, [setTheseItems, nodes]); | ||
|
||
useEffect(() => { | ||
if (window !== 'undefined' && window.innerWidth >= breakpointValues.M) { | ||
// On inital render, update carousel plugin config | ||
// to suit the non-mobile layout and functionality: | ||
setIsMobile(false); | ||
setVisibleSlides(3); | ||
} | ||
|
||
// Hook into browser's own onresize event to call our custom wrapper function: | ||
if (typeof window !== 'undefined') window.onresize = screenResize; | ||
}, [screenResize]); | ||
|
||
if (theseItems && totalSlides === null) { | ||
// Reflects our two dummy/bookend slides for non-mobile/tablet views: | ||
setTotalSlides(isMobile ? theseItems.length : theseItems.length + 2); | ||
} | ||
|
||
return ( | ||
<CarouselWrapper | ||
className="CarouselWrapper" | ||
id={thisID} | ||
mobileHeight={mobileHeight} | ||
tabletHeight={tabletHeight} | ||
desktopHeight={desktopHeight} | ||
carouselBackgroundColour={carouselBackgroundColour} | ||
> | ||
|
||
<HeadingCopyWrapper> | ||
{headingCopy} | ||
</HeadingCopyWrapper> | ||
|
||
{theseItems && ( | ||
<CarouselProvider | ||
naturalSlideWidth={50} | ||
naturalSlideHeight={200} | ||
totalSlides={totalSlides} | ||
isPlaying={autoPlay} | ||
interval={5000} | ||
visibleSlides={visibleSlides} | ||
infinite | ||
> | ||
<Slider classNameAnimation="richtext-carousel"> | ||
|
||
{/* Dummy slide for our desired non-mobile layout and functionality */} | ||
{isMobile === false && ( | ||
<Slide index={0} key={0} /> | ||
)} | ||
|
||
{Object.keys(theseItems).map((key, index) => { | ||
// Reflect that initial dummy/bookend slide shown on non-mobile/tablet views: | ||
const thisOffsetIndex = index + (isMobile ? 0 : 1); | ||
|
||
return ( | ||
// Calculate the index offset accordingly to reflect the number of slides, | ||
// but use the REAL index when determining if its the last REAL slide | ||
<Slide | ||
index={thisOffsetIndex} | ||
className={index === (theseItems.length - 1) && 'last-slide'} | ||
key={thisOffsetIndex} | ||
> | ||
|
||
<SlideCopyWrapper | ||
className="slide-copy-wrapper" | ||
mobileHeight={mobileHeight} | ||
tabletHeight={tabletHeight} | ||
desktopHeight={desktopHeight} | ||
nodeBackgroundColour={nodeBackgroundColour} | ||
nodeOutlineColour={nodeOutlineColour} | ||
> | ||
{theseItems[index].copy} | ||
</SlideCopyWrapper> | ||
|
||
</Slide> | ||
); | ||
})} | ||
|
||
{/* Dummy slide for our desired non-mobile layout and functionality */} | ||
{isMobile === false && ( | ||
<Slide index={theseItems.length + 1} key="bookend-last" /> | ||
)} | ||
|
||
</Slider> | ||
<ButtonBack>Back</ButtonBack> | ||
<ButtonNext>Next</ButtonNext> | ||
</CarouselProvider> | ||
)} | ||
|
||
</CarouselWrapper> | ||
); | ||
}; | ||
|
||
RichtextCarousel.propTypes = { | ||
data: PropTypes.shape({ | ||
headingCopy: PropTypes.string.isRequired, | ||
nodes: PropTypes.arrayOf(PropTypes.shape({ | ||
copy: PropTypes.string.isRequired | ||
})).isRequired, | ||
autoPlay: PropTypes.bool.isRequired, | ||
contentful_id: PropTypes.string.isRequired, | ||
mobileHeight: PropTypes.number, | ||
tabletHeight: PropTypes.number, | ||
desktopHeight: PropTypes.number, | ||
carouselBackgroundColour: PropTypes.string, | ||
nodeBackgroundColour: PropTypes.string, | ||
nodeOutlineColour: PropTypes.string | ||
}).isRequired | ||
}; | ||
|
||
export default RichtextCarousel; |
14 changes: 14 additions & 0 deletions
14
src/components/Organisms/RichtextCarousel/RichtextCarousel.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Richtext Carousel | ||
|
||
```js | ||
const { RichtextCarouselItems } = require('../../../styleguide/data/data'); | ||
|
||
<div> | ||
<h2 style={{textAlign: 'center'}}> | ||
Richtext Carousel #1 | ||
</h2> | ||
<RichtextCarousel | ||
data={RichtextCarouselItems}/> | ||
</div> | ||
|
||
``` |
Oops, something went wrong.