Skip to content

Commit e7f5c61

Browse files
authored
feat: added KCarousel component (#366)
* chore: init commit * chore: updated todo * chore: temp commit * chore: temp commit * chore: complete basic function * chore: complete height prop and trigger prop * chore: complete arrow prop * chore: complete loop prop * chore: complete autoplay prop、interval prop and pauseOnHover prop * chore: complete goto api、prev api and next api * chore: fix indicators jump error * chore: complete change event * chore: updated todo * test: added KCarousel unit test * docs: added KCarousel document
1 parent 69e610a commit e7f5c61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1434
-94
lines changed

.eslintignore

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ node_modules
66
.env
77
.env.*
88
!.env.example
9+
**/dist
10+
rollup.config.js
11+
play/**/*
12+
e2e/**/*
913

1014
# Ignore files for PNPM, NPM and YARN
1115
pnpm-lock.yaml
1216
package-lock.json
1317
yarn.lock
14-
**/dist
15-
rollup.config.js

.prettierignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ node_modules
66
.env
77
.env.*
88
!.env.example
9+
play/**/*
10+
e2e/**/*
911

1012
# Ignore files for PNPM, NPM and YARN
1113
pnpm-lock.yaml

components/Carousel/__test__/__snapshots__/carousel.spec.ts.snap

+27
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import { afterEach, expect, test, describe, beforeEach, vi } from 'vitest';
2+
import KCarousel from '../src';
3+
import KCarouselInit from './fixture/init.svelte';
4+
import KCarouselTriggerClick from './fixture/trigger.click.svelte';
5+
import KCarouselTriggerHover from './fixture/trigger.hover.svelte';
6+
import KCarouselAlways from './fixture/arrow.always.svelte';
7+
import KCarouselHover from './fixture/arrow.hover.svelte';
8+
import KCarouselNever from './fixture/arrow.never.svelte';
9+
import KCarouselLoop from './fixture/loop.svelte';
10+
import KCarouselPlay from './fixture/play.svelte';
11+
import KCarouselApiNext from './fixture/api.next.svelte';
12+
import KCarouselApiPrev from './fixture/api.prev.svelte';
13+
import KCarouselApiGoto from './fixture/api.goto.svelte';
14+
import KCarouselEvtApi from './fixture/event.api.svelte';
15+
import KCarouselEvt from './fixture/event.svelte';
16+
import KCarouselEvtPlay from './fixture/event.play.svelte';
17+
import KCarouselCustom from './fixture/custom.svelte';
18+
import { tick } from 'svelte';
19+
let host;
20+
21+
const initHost = () => {
22+
host = globalThis.document.createElement('div');
23+
host.setAttribute('id', 'host');
24+
globalThis.document.body.appendChild(host);
25+
};
26+
beforeEach(() => {
27+
initHost();
28+
vi.useFakeTimers();
29+
});
30+
afterEach(() => {
31+
host.remove();
32+
vi.restoreAllMocks();
33+
});
34+
35+
describe('Test: KCarousel', () => {
36+
vi.mock('svelte', async () => {
37+
const actual = (await vi.importActual('svelte')) as object;
38+
return {
39+
...actual,
40+
// @ts-ignore
41+
onMount: (await import('svelte/internal')).onMount
42+
};
43+
});
44+
test('props: cls', async () => {
45+
// @ts-ignore
46+
const instance = new KCarousel({
47+
target: host,
48+
props: {
49+
count: 3,
50+
initialIndex: 0,
51+
cls: 'k-carousel--test'
52+
}
53+
});
54+
expect(instance).toBeTruthy();
55+
expect(host!.innerHTML.includes('k-carousel--test')).toBeTruthy();
56+
expect(host.innerHTML).matchSnapshot();
57+
});
58+
59+
test('props: initialIndex', async () => {
60+
// @ts-ignore
61+
const instance = new KCarouselInit({
62+
target: host
63+
});
64+
expect(instance).toBeTruthy();
65+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
66+
expect(host.innerHTML).matchSnapshot();
67+
});
68+
69+
test('props: trigger indicators click', async () => {
70+
// @ts-ignore
71+
const instance = new KCarouselTriggerClick({
72+
target: host
73+
});
74+
expect(instance).toBeTruthy();
75+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
76+
const indicatorsElms = host.querySelectorAll('.k-carousel-indicators-item');
77+
indicatorsElms[2].click();
78+
await tick();
79+
expect(host.querySelector('[data-active="2"]')).toBeTruthy();
80+
});
81+
82+
test('props: trigger indicators hover', async () => {
83+
// @ts-ignore
84+
const instance = new KCarouselTriggerHover({
85+
target: host
86+
});
87+
expect(instance).toBeTruthy();
88+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
89+
const indicatorsElms = host.querySelectorAll('.k-carousel-indicators-item');
90+
indicatorsElms[2].dispatchEvent(new Event('mouseenter', { bubbles: true }));
91+
await tick();
92+
expect(host.querySelector('[data-active="2"]')).toBeTruthy();
93+
});
94+
95+
test('props: arrow is always', async () => {
96+
// @ts-ignore
97+
const instance = new KCarouselAlways({
98+
target: host
99+
});
100+
expect(instance).toBeTruthy();
101+
expect(host.querySelector('.k-carousel-arrow-prev')).toBeTruthy();
102+
expect(host.querySelector('.k-carousel-arrow-next')).toBeTruthy();
103+
expect(host.innerHTML).matchSnapshot();
104+
});
105+
106+
test('props: arrow is hover', async () => {
107+
// @ts-ignore
108+
const instance = new KCarouselHover({
109+
target: host
110+
});
111+
expect(instance).toBeTruthy();
112+
expect(host.querySelector('.k-carousel-arrow-prev')).not.toBeTruthy();
113+
expect(host.querySelector('.k-carousel-arrow-next')).not.toBeTruthy();
114+
const carouselElm = host.querySelector('.k-carousel');
115+
carouselElm.dispatchEvent(new Event('mouseenter', { bubbles: true }));
116+
await tick();
117+
expect(host.querySelector('.k-carousel-arrow-prev')).toBeTruthy();
118+
expect(host.querySelector('.k-carousel-arrow-next')).toBeTruthy();
119+
});
120+
121+
test('props: arrow is never', async () => {
122+
// @ts-ignore
123+
const instance = new KCarouselNever({
124+
target: host
125+
});
126+
expect(instance).toBeTruthy();
127+
expect(host.querySelector('.k-carousel-arrow-prev')).not.toBeTruthy();
128+
expect(host.querySelector('.k-carousel-arrow-next')).not.toBeTruthy();
129+
expect(host.innerHTML).matchSnapshot();
130+
});
131+
132+
test('props: loop', async () => {
133+
// @ts-ignore
134+
const instance = new KCarouselLoop({
135+
target: host
136+
});
137+
expect(instance).toBeTruthy();
138+
const carouselElm1 = host.querySelector('[test-id="loop_open"]');
139+
const carouselElm2 = host.querySelector('[test-id="loop_close"]');
140+
expect(carouselElm1.querySelector('[data-active="2"]')).toBeTruthy();
141+
expect(carouselElm2.querySelector('[data-active="2"]')).toBeTruthy();
142+
143+
const nextBtn1 = carouselElm1.querySelector('.k-carousel-arrow-next');
144+
const nextBtn2 = carouselElm2.querySelector('.k-carousel-arrow-next');
145+
nextBtn1.click();
146+
await tick();
147+
await vi.advanceTimersByTimeAsync(300);
148+
expect(carouselElm1.querySelector('[data-active="0"]')).toBeTruthy();
149+
nextBtn2.click();
150+
await tick();
151+
await vi.advanceTimersByTimeAsync(300);
152+
expect(carouselElm2.querySelector('[data-active="2"]')).toBeTruthy();
153+
154+
const prevBtn1 = carouselElm1.querySelector('.k-carousel-arrow-prev');
155+
prevBtn1.click();
156+
await tick();
157+
await vi.advanceTimersByTimeAsync(300);
158+
expect(carouselElm1.querySelector('[data-active="2"]')).toBeTruthy();
159+
});
160+
161+
test('props: autoplay & interval & pauseOnHover', async () => {
162+
// @ts-ignore
163+
const instance = new KCarouselPlay({
164+
target: host
165+
});
166+
expect(instance).toBeTruthy();
167+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
168+
await tick();
169+
await vi.advanceTimersByTimeAsync(300);
170+
expect(host.querySelector('[data-active="2"]')).toBeTruthy();
171+
await tick();
172+
await vi.advanceTimersByTimeAsync(300);
173+
expect(host.querySelector('[data-active="0"]')).toBeTruthy();
174+
await tick();
175+
await vi.advanceTimersByTimeAsync(300);
176+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
177+
178+
const carouselElm = host.querySelector('.k-carousel');
179+
carouselElm.dispatchEvent(new Event('mouseenter', { bubbles: true }));
180+
await tick();
181+
await vi.advanceTimersByTimeAsync(600);
182+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
183+
});
184+
185+
test('api: goto', async () => {
186+
// @ts-ignore
187+
const instance = new KCarouselApiGoto({
188+
target: host
189+
});
190+
expect(instance).toBeTruthy();
191+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
192+
const btn = host.querySelector('button');
193+
btn.click();
194+
await tick();
195+
expect(host.querySelector('[data-active="2"]')).toBeTruthy();
196+
});
197+
198+
test('api: prev', async () => {
199+
// @ts-ignore
200+
const instance = new KCarouselApiPrev({
201+
target: host
202+
});
203+
expect(instance).toBeTruthy();
204+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
205+
const btn = host.querySelector('button');
206+
btn.click();
207+
await tick();
208+
expect(host.querySelector('[data-active="0"]')).toBeTruthy();
209+
});
210+
211+
test('api: next', async () => {
212+
// @ts-ignore
213+
const instance = new KCarouselApiNext({
214+
target: host
215+
});
216+
expect(instance).toBeTruthy();
217+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
218+
const btn = host.querySelector('button');
219+
btn.click();
220+
await tick();
221+
expect(host.querySelector('[data-active="2"]')).toBeTruthy();
222+
});
223+
test('event: change event caused by arrow and indicators', async () => {
224+
// @ts-ignore
225+
const instance = new KCarouselEvt({
226+
target: host
227+
});
228+
expect(instance).toBeTruthy();
229+
expect(host.querySelector('[data-active="1"]')).toBeTruthy();
230+
const indicatorsElms = host.querySelectorAll('.k-carousel-indicators-item');
231+
indicatorsElms[2].click();
232+
await tick();
233+
expect(host.innerHTML).toMatchSnapshot('{"index":2,"oldIndex":1}');
234+
const nextBtn = host.querySelector('.k-carousel-arrow-next');
235+
nextBtn.click();
236+
await tick();
237+
expect(host.innerHTML).toMatchSnapshot('{"index":1,"oldIndex":2}');
238+
const prevBtn = host.querySelector('.k-carousel-arrow-prev');
239+
prevBtn.click();
240+
await tick();
241+
expect(host.innerHTML).toMatchSnapshot('{"index":2,"oldIndex":1}');
242+
});
243+
244+
test('event: change event caused by auto play', async () => {
245+
// @ts-ignore
246+
const instance = new KCarouselEvtPlay({
247+
target: host
248+
});
249+
expect(instance).toBeTruthy();
250+
await tick();
251+
await vi.advanceTimersByTimeAsync(300);
252+
expect(host.innerHTML).toMatchSnapshot('{"index":2,"oldIndex":1}');
253+
await tick();
254+
await vi.advanceTimersByTimeAsync(300);
255+
expect(host.innerHTML).toMatchSnapshot('{"index":1,"oldIndex":2}');
256+
});
257+
258+
test('event: change event caused by api', async () => {
259+
// @ts-ignore
260+
const instance = new KCarouselEvtApi({
261+
target: host
262+
});
263+
expect(instance).toBeTruthy();
264+
const btnGoto = host.querySelector('#btn_goto');
265+
btnGoto.click();
266+
await tick();
267+
expect(host.innerHTML).toMatchSnapshot('{"index":2,"oldIndex":1}');
268+
269+
const btnNext = host.querySelector('#btn_next');
270+
btnNext.click();
271+
await tick();
272+
expect(host.innerHTML).toMatchSnapshot('{"index":0,"oldIndex":2}');
273+
274+
const btnPrev = host.querySelector('#btn_prev');
275+
btnPrev.click();
276+
await tick();
277+
expect(host.innerHTML).toMatchSnapshot('{"index":2,"oldIndex":0}');
278+
});
279+
280+
test('slot: custom arrow indicators ', async () => {
281+
// @ts-ignore
282+
const instance = new KCarouselCustom({
283+
target: host
284+
});
285+
expect(instance).toBeTruthy();
286+
expect(host.innerHTML).matchSnapshot();
287+
});
288+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
let KCarouselRef: any = null;
4+
const handleClick = () => {
5+
KCarouselRef.goto(2);
6+
};
7+
</script>
8+
9+
<KCarousel autoplay={false} count={3} bind:this={KCarouselRef} initialIndex={1}>
10+
<div style:height="280px" class="bg-green-400/15"></div>
11+
<div style:height="280px" class="bg-red-500/15"></div>
12+
<div style:height="280px" class="bg-blue-600/15"></div>
13+
</KCarousel>
14+
<button on:click={handleClick}>goto</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
let KCarouselRef: any = null;
4+
const handleClick = () => {
5+
KCarouselRef.next();
6+
};
7+
</script>
8+
9+
<KCarousel autoplay={false} count={3} bind:this={KCarouselRef} initialIndex={1}>
10+
<div style:height="280px" class="bg-green-400/15"></div>
11+
<div style:height="280px" class="bg-red-500/15"></div>
12+
<div style:height="280px" class="bg-blue-600/15"></div>
13+
</KCarousel>
14+
<button on:click={handleClick}>next</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
let KCarouselRef: any = null;
4+
const handleClick = () => {
5+
KCarouselRef.prev();
6+
};
7+
</script>
8+
9+
<KCarousel autoplay={false} count={3} bind:this={KCarouselRef} initialIndex={1}>
10+
<div style:height="280px" class="bg-green-400/15"></div>
11+
<div style:height="280px" class="bg-red-500/15"></div>
12+
<div style:height="280px" class="bg-blue-600/15"></div>
13+
</KCarousel>
14+
<button on:click={handleClick}>prev</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
</script>
4+
5+
<KCarousel autoplay={false} arrow="always" count={3} initialIndex={1}>
6+
<div style:height="280px" class="bg-green-400/15"></div>
7+
<div style:height="280px" class="bg-red-500/15"></div>
8+
<div style:height="280px" class="bg-blue-600/15"></div>
9+
</KCarousel>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
</script>
4+
5+
<KCarousel autoplay={false} arrow="hover" count={3} initialIndex={1}>
6+
<div style:height="280px" class="bg-green-400/15"></div>
7+
<div style:height="280px" class="bg-red-500/15"></div>
8+
<div style:height="280px" class="bg-blue-600/15"></div>
9+
</KCarousel>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
import { KCarousel } from '@ikun-ui/carousel';
3+
</script>
4+
5+
<KCarousel autoplay={false} arrow="never" count={3} initialIndex={1}>
6+
<div style:height="280px" class="bg-green-400/15"></div>
7+
<div style:height="280px" class="bg-red-500/15"></div>
8+
<div style:height="280px" class="bg-blue-600/15"></div>
9+
</KCarousel>

0 commit comments

Comments
 (0)