Skip to content

Commit

Permalink
feat(Carousel): add full responsive carousel component
Browse files Browse the repository at this point in the history
affects: @crave/farmblocks-carousel

BREAKING CHANGE:
add infinite scroll option; add number of slides displayed; add number of slides displayed by
breakpoints; remove imageSet prop; add slides prop that accepts an array of nodes; modify carousel
layout; add dots to navigate through slides; add arrows to navigate through slides

ISSUES CLOSED: #1043
  • Loading branch information
juliansotuyo committed Jun 15, 2021
1 parent 6789e0a commit f4d8d9e
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 86 deletions.
53 changes: 23 additions & 30 deletions packages/carousel/src/Carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,35 @@ import {
Content,
ContentWrapper,
} from "./styledComponents/Carousel";

import Slide from "./components/Slide";
import ArrowButton from "./components/ArrowButton";
import Dots from "./components/Dots";
import useResizeWindow from "./hooks/useResizeWindow";
import useTouch from "./hooks/useTouch";

function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
function Carousel({ slides, slidesToShow, infiniteLoop, breakpoints }) {
const [displayNumber, setDisplayNumber] = useState(
slidesToShow < imageSet.length ? slidesToShow : imageSet.length,
slidesToShow < slides.length ? slidesToShow : slides.length,
);

const [dotIndex, setDotIndex] = useState(0);
const [currentIndex, setCurrentIndex] = useState(
infiniteLoop && displayNumber < imageSet.length ? displayNumber : 0,
infiniteLoop && displayNumber < slides.length ? displayNumber : 0,
);

useResizeWindow({
displayNumber,
setDisplayNumber,
setCurrentIndex,
dotIndex,
numberOfCards: imageSet.length,
numberOfCards: slides.length,
slidesToShow,
breakpoints,
infiniteLoop,
});

const totalOfCards = imageSet.length;
const isRepeating = infiniteLoop && imageSet.length > displayNumber;
const totalOfCards = slides.length;
const isRepeating = infiniteLoop && slides.length > displayNumber;
const [transitionEnabled, setTransitionEnabled] = useState(true);

useEffect(() => {
Expand Down Expand Up @@ -90,14 +91,9 @@ function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
const output = [];
for (let index = 0; index < displayNumber; index += 1) {
output.push(
<div key={imageSet[totalOfCards - 1 - index].image}>
<div style={{ padding: 10 }}>
<img
src={imageSet[totalOfCards - 1 - index].image}
alt={imageSet[totalOfCards - 1 - index].name}
/>
</div>
</div>,
<Slide key={slides[totalOfCards - 1 - index].id}>
{slides[totalOfCards - 1 - index].content}
</Slide>,
);
}
output.reverse();
Expand All @@ -108,11 +104,7 @@ function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
const output = [];
for (let index = 0; index < displayNumber; index += 1) {
output.push(
<div key={imageSet[index].image}>
<div style={{ padding: 10 }}>
<img src={imageSet[index].image} alt={imageSet[index].name} />
</div>
</div>,
<Slide key={slides[index].id}>{slides[index].content}</Slide>,
);
}
return output;
Expand All @@ -135,12 +127,8 @@ function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
onTransitionEnd={handleTransitionEnd}
>
{totalOfCards > displayNumber && isRepeating && renderExtraPrev()}
{imageSet.map((item) => (
<div key={item.image}>
<div style={{ padding: 10 }}>
<img src={item.image} alt={item.name} />
</div>
</div>
{slides.map((item) => (
<Slide key={item.id}>{item.content}</Slide>
))}
{totalOfCards > displayNumber && isRepeating && renderExtraNext()}
</Content>
Expand All @@ -155,7 +143,7 @@ function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
</Wrapper>
{isRepeating && (
<Dots
imageSet={imageSet}
slides={slides}
handleClick={handleDotClick}
selectedDot={dotIndex}
/>
Expand All @@ -165,15 +153,20 @@ function Carousel({ imageSet, slidesToShow, infiniteLoop }) {
}

Carousel.propTypes = {
imageSet: PropTypes.arrayOf(
PropTypes.shape({ image: PropTypes.string, name: PropTypes.string }),
slides: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
content: PropTypes.node,
}),
).isRequired,
slidesToShow: PropTypes.number,
breakpoints: PropTypes.arrayOf(PropTypes.number),
infiniteLoop: PropTypes.bool,
};
Carousel.defaultProps = {
slidesToShow: 3,
infiniteLoop: true,
breakpoints: [1, 2],
infiniteLoop: false,
};

export default Carousel;
51 changes: 29 additions & 22 deletions packages/carousel/src/Carousel.story.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,70 @@ import Carousel from ".";

const imageSet = [
{
image: "https://picsum.photos/640/?image=1080",
name: "Organic Pepper",
id: "organic-pepper",
content: (
<img src="https://picsum.photos/640/?image=1080" alt="Organic Pepper" />
),
},
{
image: "https://picsum.photos/640/?image=824",
name: "Tomato",
id: "tomato",
content: <img src="https://picsum.photos/640/?image=824" alt="Tomato" />,
},

{
image: "https://picsum.photos/640/?image=889",
name: "Grapefruit",
id: "grapefruit",
content: (
<img src="https://picsum.photos/640/?image=889" alt="Grapefruit" />
),
},
{
image: "https://picsum.photos/640/?image=674",
name: "Tomato",
id: "tomato-2",
content: <img src="https://picsum.photos/640/?image=674" alt="Tomato" />,
},
{
image: "https://picsum.photos/640/?image=292",
name: "Tomato",
id: "tomato-3",
content: <img src="https://picsum.photos/640/?image=292" alt="Tomato" />,
},
{
image: "https://picsum.photos/640/?image=517",
name: "Tomato",
id: "tomato-4",
content: <img src="https://picsum.photos/640/?image=517" alt="Tomato" />,
},
{
image: "https://picsum.photos/640/?image=627",
name: "Tomato",
id: "tomato-5",
content: <img src="https://picsum.photos/640/?image=627" alt="Tomato" />,
},
{
image: "https://picsum.photos/640/?image=75",
name: "Tomato",
id: "tomato-6",
content: <img src="https://picsum.photos/640/?image=75" alt="Tomato" />,
},
{
image: "https://picsum.photos/640/?image=766",
name: "Romaine Lettuce",
id: "romaine-lettuce",
content: (
<img src="https://picsum.photos/640/?image=766" alt="Romaine Lettuce" />
),
},
];

storiesOf("Carousel", module)
.add("1 photo", () => (
<Carousel
imageSet={imageSet.slice(0, 1)}
slides={imageSet.slice(0, 1)}
infiniteLoop={false}
slidesToShow={1}
/>
))

.add("2 photos", () => (
<Carousel
imageSet={imageSet.slice(0, 2)}
slides={imageSet.slice(0, 2)}
infiniteLoop={false}
slidesToShow={2}
/>
))

.add("all photos", () => (
<Carousel imageSet={imageSet} infiniteLoop={false} slidesToShow={3} />
<Carousel slides={imageSet} infiniteLoop={false} slidesToShow={3} />
))
.add("infinite loop", () => (
<Carousel imageSet={imageSet} infiniteLoop slidesToShow={3} />
<Carousel slides={imageSet} infiniteLoop slidesToShow={3} />
));
38 changes: 19 additions & 19 deletions packages/carousel/src/Carousel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ describe("Carousel", () => {

it("should render component without arrows and dots", () => {
const initialValues = {
imageSet: [
slides: [
{
image: "image1.png",
name: "image1.png",
id: "image1",
content: <img src="image1.png" alt="image1.png" />,
},
{
image: "image2.png",
name: "image2.png",
id: "image2",
content: <img src="image2.png" alt="image2.png" />,
},
],
slidesToShow: 2,
Expand All @@ -32,18 +32,18 @@ describe("Carousel", () => {
});
it("should render component with arrows", () => {
const initialValues = {
imageSet: [
slides: [
{
image: "image1.png",
name: "image1.png",
id: "image1",
content: <img src="image1.png" alt="image1.png" />,
},
{
image: "image2.png",
name: "image2.png",
id: "image2",
content: <img src="image2.png" alt="image2.png" />,
},
{
image: "image3.png",
name: "image3.png",
id: "image3",
content: <img src="image3.png" alt="image3.png" />,
},
],
slidesToShow: 2,
Expand All @@ -68,18 +68,18 @@ describe("Carousel", () => {

it("should render component with infinite scroll", async () => {
const initialValues = {
imageSet: [
slides: [
{
image: "image1.png",
name: "image1.png",
id: "image1",
content: <img src="image1.png" alt="image1.png" />,
},
{
image: "image2.png",
name: "image2.png",
id: "image2",
content: <img src="image2.png" alt="image2.png" />,
},
{
image: "image3.png",
name: "image3.png",
id: "image3",
content: <img src="image3.png" alt="image3.png" />,
},
],
slidesToShow: 2,
Expand Down
13 changes: 8 additions & 5 deletions packages/carousel/src/components/Dots.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React from "react";
import PropTypes from "prop-types";
import { Dot, DotsContainer } from "../styledComponents/Dots";

function Dots({ imageSet, handleClick, selectedDot }) {
function Dots({ slides, handleClick, selectedDot }) {
return (
<DotsContainer data-testid="dots-container">
{imageSet.map((item, index) => (
{slides.map((item, index) => (
<Dot
key={item.image}
key={item.id}
onClick={() => handleClick(index)}
className={selectedDot === index ? "active" : ""}
/>
Expand All @@ -17,8 +17,11 @@ function Dots({ imageSet, handleClick, selectedDot }) {
}

Dots.propTypes = {
imageSet: PropTypes.arrayOf(
PropTypes.shape({ image: PropTypes.string, name: PropTypes.string }),
slides: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
content: PropTypes.node,
}),
).isRequired,
handleClick: PropTypes.func,
selectedDot: PropTypes.number,
Expand Down
16 changes: 16 additions & 0 deletions packages/carousel/src/components/Slide.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import PropTypes from "prop-types";

import { Container, Wrapper } from "../styledComponents/Slide";

const Slide = ({ children }) => (
<Wrapper>
<Container>{children}</Container>
</Wrapper>
);

Slide.propTypes = {
children: PropTypes.node.isRequired,
};

export default Slide;
13 changes: 9 additions & 4 deletions packages/carousel/src/hooks/useResizeWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const useResizeWindow = ({
dotIndex,
numberOfCards,
slidesToShow,
breakpoints,
infiniteLoop,
}) => {
const [screendWidth, setScreenWidth] = useState(window.innerWidth);
Expand All @@ -23,7 +24,7 @@ const useResizeWindow = ({
let incrementIndex = infiniteLoop ? 2 : 0;

if (screenSize < 768) {
setDisplayNumber(1);
setDisplayNumber(breakpoints[0]);
if (infiniteLoop) incrementIndex = 1;
setCurrentIndex(
dotIndex + incrementIndex < numberOfCards
Expand All @@ -32,14 +33,17 @@ const useResizeWindow = ({
);
} else if (displayNumber < numberOfCards) {
if (screenSize < 1200) {
setDisplayNumber(2);
setDisplayNumber(breakpoints[1]);
setCurrentIndex(numberOfCards > 2 ? dotIndex + incrementIndex : 0);
} else if (slidesToShow > 2 && numberOfCards >= slidesToShow) {
} else if (
slidesToShow > breakpoints[1] &&
numberOfCards >= slidesToShow
) {
setDisplayNumber(slidesToShow);
if (infiniteLoop) incrementIndex = slidesToShow;
setCurrentIndex(dotIndex + incrementIndex);
} else {
setDisplayNumber(2);
setDisplayNumber(breakpoints[1]);
setCurrentIndex(dotIndex + incrementIndex);
}
}
Expand All @@ -64,6 +68,7 @@ useResizeWindow.propTypes = {
dotIndex: PropTypes.number,
numberOfCards: PropTypes.number,
slidesToShow: PropTypes.number,
breakpoints: PropTypes.arrayOf(PropTypes.number),
infiniteLoop: PropTypes.bool,
};

Expand Down
Loading

0 comments on commit f4d8d9e

Please sign in to comment.