Skip to content

Commit

Permalink
Sliding Component (#209)
Browse files Browse the repository at this point in the history
* feat: sliding component create

* fix: rename parameter in add and remove functions

* chore: init refactor sliding component

* refactor: refactor sliding component

* chore: implements error handling in remove method

* chore: add controls in storybook

* fix: delete some blank lines

* chore: change slide css and remove updateWidth function

* refactor: refactor testes

* refactor: refactor sliding tests

* test: adjusted the sliding tests

* test: sliding tests fixed

* test: adjust sliding unmount test

* chore: remove this.slides and add activeSlide method

* fix: create swipeLet and swipeRight methods

---------

Co-authored-by: Alexandre Gomes <[email protected]>
  • Loading branch information
DominMFD and Alecell committed Jun 3, 2024
1 parent 851d256 commit 61aaa7f
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 0 deletions.
108 changes: 108 additions & 0 deletions src/components/Sliding/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Component } from 'pet-dex-utilities';
import { makeSwipable } from '../../utils/swiper';
import './index.scss';

const events = [
'slide:add',
'slide:next',
'slide:previous',
'slide:remove',
'slides:clear',
];

const activeSlide = (slides, slide) => {
Array.from(slides).forEach((item) => {
item.classList.toggle('sliding__slide--active', item === slide);
});
};

const html = `
<div class="sliding" data-select="sliding">
<div class="sliding__content" data-select="sliding-content">
</div>
</div>`;

export default function Sliding({ slides = [] }) {
Component.call(this, { html, events });

this.slideIndex = 0;

slides.forEach((item) => this.add(item));

const $sliding = this.selected.get('sliding');

makeSwipable($sliding);

this.swipeLeft = () => {
this.next();
};

this.swipeRight = () => {
this.previous();
};

this.listen('mount', () => {
activeSlide(
Array.from(this.selected.get('sliding-content').children),
this.selected.get('sliding-content').children[0],
);
$sliding.addEventListener('swipe-left', this.swipeLeft());
$sliding.addEventListener('swipe-right', this.swipeRight());
});
this.listen('unmount', () => {
$sliding.removeEventListener('swipe-left', this.swipeLeft());
$sliding.removeEventListener('swipe-right', this.swipeRight());
});
}

Sliding.prototype = Object.assign(Sliding.prototype, Component.prototype, {
add(slide) {
slide.classList.add('sliding__slide');
this.selected.get('sliding-content').appendChild(slide);

this.emit('slide:add', slide);
},

remove(slide) {
this.selected.get('sliding-content').removeChild(slide);

this.emit('slide:remove', slide);
},

next() {
this.slideIndex += 1;
const slides = this.selected.get('sliding-content').children;

if (this.slideIndex > slides.length - 1) this.slideIndex = 0;

const slide = slides[this.slideIndex];
const container = this.selected.get('sliding').clientWidth;
this.selected.get('sliding-content').style.transform =
`translateX(${-this.slideIndex * container}px)`;
activeSlide(slides, slide);
this.emit('slide:next', slide);
},

previous() {
this.slideIndex -= 1;
const slides = this.selected.get('sliding-content').children;

if (this.slideIndex < 0) this.slideIndex = slides.length - 1;

const slide = slides[this.slideIndex];
const container = this.selected.get('sliding').clientWidth;

this.selected.get('sliding-content').style.transform =
`translateX(${-this.slideIndex * container}px)`;
activeSlide(slides, slide);
this.emit('slide:previous', slide);
},

clear() {
Array.from(this.selected.get('sliding-content').children).forEach((slide) =>
this.remove(slide),
);

this.emit('slides:clear');
},
});
20 changes: 20 additions & 0 deletions src/components/Sliding/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.sliding {
width: 100%;
overflow: hidden;

margin: auto;

position: relative;

&__content {
display: flex;

transform: translateX(0);

transition: transform 0.6s ease-in-out;
}

&__slide {
flex: 0 0 100%;
}
}
127 changes: 127 additions & 0 deletions src/components/Sliding/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { describe, expect, it } from 'vitest';
import { render, screen } from '@testing-library/vanilla';
import Slinding from '.';

const $slide1 = document.createElement('div');
$slide1.style.height = '200px';
$slide1.style.backgroundColor = 'red';
$slide1.textContent = 'slide 1';

const $slide2 = document.createElement('div');
$slide2.style.height = '200px';
$slide2.style.backgroundColor = 'pink';
$slide2.textContent = 'slide 2';

const $slide3 = document.createElement('div');
$slide3.style.height = '200px';
$slide3.style.backgroundColor = 'green';
$slide3.textContent = 'slide 3';

const $slide4 = document.createElement('div');
$slide4.style.height = '200px';
$slide4.style.backgroundColor = 'black';
$slide4.textContent = 'slide 4';

const propsMock = {
slides: [$slide1, $slide2, $slide3],
};

const makeSut = (parameters) => render(new Slinding(parameters));

describe('Slide', () => {
describe('on mount', () => {
it('renders with items passed by props', async () => {
makeSut(propsMock);
const slide1 = await screen.findByText('slide 1');
const slide2 = await screen.findByText('slide 2');
const slide3 = await screen.findByText('slide 3');

expect(slide1).toBeInTheDocument();
expect(slide2).toBeInTheDocument();
expect(slide3).toBeInTheDocument();
});
});

describe('on unmount', () => {
it('clear items', () => {
const element = makeSut(propsMock);

const callback = vi.fn();
element.listen('unmount', callback);

const slide1 = screen.queryByText('slide 1');
const slide2 = screen.queryByText('slide 2');
const slide3 = screen.queryByText('slide 3');

element.unmount();

expect(callback).toBeCalledWith();
expect(slide1).not.toBeInTheDocument();
expect(slide2).not.toBeInTheDocument();
expect(slide3).not.toBeInTheDocument();
});
});

it('add item programmatically', async () => {
const sliding = makeSut(propsMock);

const callback = vi.fn();
sliding.listen('slide:add', callback);
sliding.add($slide4);

const slide4 = await screen.findByText('slide 4');

expect(callback).toBeCalledWith($slide4);
expect(slide4).toBeInTheDocument();
});

it('remove item programmatically', () => {
const sliding = makeSut(propsMock);

const callback = vi.fn();
sliding.listen('slide:remove', callback);
sliding.remove($slide2);

const slide2 = screen.queryByText('slide 2');

expect(slide2).not.toBeInTheDocument();
expect(callback).toHaveBeenCalledWith($slide2);
});

it('next item programmatically', () => {
const sliding = makeSut(propsMock);

const callback = vi.fn();
sliding.listen('slide:next', callback);
sliding.next();

expect(callback).toBeCalledWith($slide2);
});

it('previous item programmatically', () => {
const sliding = makeSut(propsMock);

const callback = vi.fn();
sliding.listen('slide:previous', callback);
sliding.previous();

expect(callback).toBeCalledWith($slide3);
});

it('clear items programmatically', () => {
const sliding = makeSut(propsMock);

const callback = vi.fn();
sliding.listen('slides:clear', callback);
sliding.clear();

const slide1 = screen.queryByText('slide 1');
const slide2 = screen.queryByText('slide 2');
const slide3 = screen.queryByText('slide 3');

expect(callback).toBeCalledWith();
expect(slide1).not.toBeInTheDocument();
expect(slide2).not.toBeInTheDocument();
expect(slide3).not.toBeInTheDocument();
});
});
52 changes: 52 additions & 0 deletions src/stories/Sliding.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { initializeSwiper } from '../utils/swiper';
import Sliding from '../components/Sliding';
import Button from '../components/Button';

const $slide1 = document.createElement('div');
$slide1.style.height = '200px';
$slide1.style.backgroundColor = 'red';

const $slide2 = document.createElement('div');
$slide2.style.height = '200px';
$slide2.style.backgroundColor = 'pink';

const $slide3 = document.createElement('div');
$slide3.style.height = '200px';
$slide3.style.backgroundColor = 'green';

const button4 = new Button({
text: '<',
isFullWidth: false,
});

const button5 = new Button({
text: '>',
isFullWidth: false,
});

export default {
title: 'Components/Sliding',
render: (args) => {
const sliding = new Sliding(args);
const $container = document.createElement('div');
initializeSwiper();
window.sliding = sliding;
sliding.mount($container);
button4.mount($container);
button5.mount($container);

button4.listen('click', () => sliding.previous());
button5.listen('click', () => sliding.next());

return $container;
},
argsTypes: {
slides: { control: 'object', defaultValue: [] },
},
};

export const Default = {
args: {
slides: [$slide1, $slide2, $slide3],
},
};

0 comments on commit 61aaa7f

Please sign in to comment.