-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0a6e437
commit 5e75e7a
Showing
6 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,70 @@ | ||
import ImageSlider from './ImageSlider'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
// Declaring images array | ||
const images = [ | ||
'https://images.pexels.com/photos/248547/pexels-photo-248547.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', | ||
'https://images.pexels.com/photos/12838/pexels-photo-12838.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', | ||
'https://images.pexels.com/photos/161172/cycling-bike-trail-sport-161172.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', | ||
]; | ||
|
||
const meta = { | ||
title: 'Components/ImageSlider', | ||
component: ImageSlider, | ||
} satisfies Meta<typeof ImageSlider>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
|
||
const defaultProps = { | ||
|
||
}; | ||
|
||
const disableControls = { | ||
parameters: { | ||
controls: { | ||
disable: true | ||
}, | ||
actions: { | ||
disable: true | ||
}, | ||
} | ||
}; | ||
|
||
export const Demo: Story = { | ||
args: { | ||
...defaultProps, | ||
images: images, | ||
size: 'large', | ||
}, | ||
tags: ['excludeFromSidebar'] | ||
}; | ||
|
||
export const Default: Story = { | ||
args: { | ||
...defaultProps, | ||
images: images, | ||
size: 'large', | ||
}, | ||
...disableControls | ||
}; | ||
|
||
export const Large: Story = { | ||
args: { | ||
...defaultProps, | ||
images: images, | ||
size: 'large', | ||
}, | ||
...disableControls | ||
}; | ||
|
||
export const Small: Story = { | ||
args: { | ||
...defaultProps, | ||
images: images, | ||
size: 'small', | ||
}, | ||
...disableControls | ||
}; | ||
|
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,70 @@ | ||
import styled from 'styled-components'; | ||
|
||
export const StyledImageSlider = styled.div<{ size: 'small' | 'large' }>` | ||
.slider-container { | ||
position: relative; | ||
width: 100%; | ||
max-width: ${props => (props.size === 'small' ? '400px' : '800px')}; | ||
height: ${props => (props.size === 'small' ? '250px' : '500px')}; | ||
margin: 0 auto; | ||
overflow: hidden; | ||
} | ||
.slider-image { | ||
object-fit: cover; | ||
width: 100%; | ||
height: 100%; | ||
aspect-ratio: 10/6; | ||
display: block; | ||
flex-shrink: 0; | ||
flex-grow: 0; | ||
} | ||
.slider-button { | ||
all: unset; | ||
display: block; | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
padding: 0.8rem; | ||
cursor: pointer; | ||
transition: background-color 100ms ease-in-out, transform 100ms ease; /* Added transform transition */ | ||
} | ||
.slider-button:hover { | ||
background-color: rgba(0, 0, 0, 0.09); | ||
} | ||
.slider-button > * { | ||
stroke: white; | ||
width: 2rem; | ||
height: 2rem; | ||
transform: scale(${props => (props.size === 'small' ? '0.8' : '1')}); /* Scale based on size prop */ | ||
} | ||
.slider-index-button { | ||
all: unset; | ||
display: block; | ||
cursor: pointer; | ||
width: 1rem; | ||
height: 1rem; | ||
transition: scale 100ms ease-in-out; | ||
transform: scale(${props => (props.size === 'small' ? '0.8' : '1')}); /* Scale based on size prop */ | ||
} | ||
.slider-index-button:hover { | ||
scale: 1.2; | ||
} | ||
.slider-index-button > * { | ||
stroke: white; | ||
height: 100%; | ||
width: 100%; | ||
} | ||
.turn-horizontal { | ||
height: ${props => (props.size === 'small' ? '250px' : '500px')}; | ||
transition: transform 0.5s ease; | ||
display: flex; | ||
} | ||
`; |
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,12 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import ImageSlider from './ImageSlider'; | ||
|
||
describe('<ImageSlider />', () => { | ||
it('should mount', () => { | ||
render(<ImageSlider/>); | ||
|
||
const imageSlider = screen.getByTestId('ImageSlider'); | ||
|
||
expect(imageSlider).toBeInTheDocument(); | ||
}); | ||
}); |
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,58 @@ | ||
import { FC, useState } from 'react'; | ||
import { StyledImageSlider } from './ImageSlider.style'; | ||
import { ArrowBigLeft, ArrowBigRight, Circle, CircleDot } from 'lucide-react'; | ||
|
||
|
||
|
||
|
||
type ImageSliderProps = { | ||
images: string[]; // Array of image URLs | ||
size: 'small' | 'large'; // Define the size prop | ||
} | ||
|
||
const ImageSlider: FC<ImageSliderProps> = ({ images, size }:ImageSliderProps ) => { | ||
// State to track current image index | ||
const [currentImageIndex, setCurrentImageIndex] = useState(0); | ||
// Function to navigate to previous slide | ||
const goToPreviousSlide = () => { | ||
const newIndex = (currentImageIndex - 1 + images.length) % images.length; | ||
setCurrentImageIndex(newIndex); | ||
}; | ||
// Function to navigate to next slide | ||
const goToNextSlide = () => { | ||
const newIndex = (currentImageIndex + 1) % images.length; | ||
setCurrentImageIndex(newIndex); | ||
}; | ||
|
||
return ( | ||
<StyledImageSlider data-testid="ImageSlider" size={size}> | ||
<div className="slider-container"> | ||
{/* Slider container div */} | ||
<div className="turn-horizontal" style={{ | ||
width: `${100 * images.length}%`, // Set the width to accommodate all images side by side | ||
transform: `translateX(-${currentImageIndex * (100 / images.length)}%)`, // Move the container horizontally to show the current slide | ||
}}> | ||
{/* Render images */} | ||
{images.map((url, index) => ( | ||
<img key={index} src={url} alt={`Slide ${index}`} className="slider-image" style={{ width: `${100 / images.length}%` }} /> | ||
))} | ||
</div> | ||
{/* Navigation buttons */} | ||
<button onClick={goToPreviousSlide} className="slider-button" style={{ left: 0 }}> | ||
<ArrowBigLeft /> | ||
</button> | ||
<button onClick={goToNextSlide} className="slider-button" style={{ right: 0 }}> | ||
<ArrowBigRight /> | ||
</button> | ||
{/* Index buttons */} | ||
<div style={{ position: 'absolute', bottom: '1.5rem', left: '50%', translate: '-50%', display: 'flex', gap: '.25rem', }}> | ||
{images.map((_, index) => ( | ||
<button key={index} className="slider-index-button" onClick={() => setCurrentImageIndex(index)} > {index === currentImageIndex ? <CircleDot/> : <Circle/> } </button> | ||
))} | ||
</div> | ||
</div> | ||
</StyledImageSlider> | ||
); | ||
}; | ||
|
||
export default ImageSlider; |