diff --git a/devui/carousel/__tests__/carousel.spec.ts b/devui/carousel/__tests__/carousel.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..525d71d7692531a36fe0c585f59b2a3117ccff7d
--- /dev/null
+++ b/devui/carousel/__tests__/carousel.spec.ts
@@ -0,0 +1,237 @@
+import { ref, nextTick } from 'vue'
+import { mount } from '@vue/test-utils';
+import Carousel from '../carousel';
+import CarouselItem from '../item';
+import Button from '../../button'
+
+const wait = (ms = 100) =>
+ new Promise(resolve => setTimeout(() => resolve(), ms))
+
+describe('d-carousel', () => {
+ it('arrowTrigger-never', () => {
+ const wrapper = mount(Carousel, {
+ props: {
+ arrowTrigger: 'never',
+ height: '200px',
+ },
+ });
+ expect(wrapper.find('.devui-carousel-arrow').exists()).toBe(false);
+ });
+
+ it('arrowTrigger-hover-out', () => {
+ const wrapper = mount(Carousel, {
+ props: {
+ arrowTrigger: 'hover',
+ height: '200px',
+ },
+ });
+ expect(wrapper.find('.devui-carousel-arrow').exists()).toBe(false);
+ });
+ it('arrowTrigger-hover-in', async () => {
+ const wrapper = mount(Carousel, {
+ props: {
+ arrowTrigger: 'hover',
+ height: '200px',
+ },
+ });
+ wrapper.find('.devui-carousel-container').trigger('mouseenter')
+ await nextTick()
+ expect(wrapper.find('.devui-carousel-arrow').exists()).toBe(true);
+ });
+
+ it('arrowTrigger-always', () => {
+ const wrapper = mount(Carousel, {
+ props: {
+ arrowTrigger: 'always',
+ height: '200px',
+ },
+ });
+ expect(wrapper.find('.devui-carousel-arrow').exists()).toBe(true);
+ });
+
+ it('showDots-false', () => {
+ const wrapper = mount(Carousel, {
+ props: {
+ showDots: false,
+ height: '200px',
+ },
+ });
+ expect(wrapper.find('.devui-carousel-dots').exists()).toBe(false);
+ });
+
+ it('showDots-click', async () => {
+ const wrapper = mount({
+ components: {
+ 'd-carousel': Carousel,
+ 'd-carousel-item': CarouselItem,
+ },
+ template: `
+
+ Page 1
+ Page 2
+ Page 3
+ Page 4
+
+ `,
+ setup() {
+ const activeIndex = ref(0)
+
+ const onChange = (index: number) => {
+ activeIndex.value = index
+ }
+
+ return {
+ activeIndex,
+
+ onChange,
+ }
+ }
+ });
+
+ await nextTick()
+ wrapper.findAll('.dot-item')[1].trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(1);
+ });
+
+ it('showDots-enter', async () => {
+ const wrapper = mount({
+ components: {
+ 'd-carousel': Carousel,
+ 'd-carousel-item': CarouselItem,
+ },
+ template: `
+
+ Page 1
+ Page 2
+ Page 3
+ Page 4
+
+ `,
+ setup() {
+ const activeIndex = ref(0)
+
+ const onChange = (index: number) => {
+ activeIndex.value = index
+ }
+
+ return {
+ activeIndex,
+
+ onChange,
+ }
+ }
+ });
+ await nextTick()
+ wrapper.findAll('.dot-item')[1].trigger('mouseenter')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(1);
+ });
+
+ it('operate', async () => {
+ const wrapper = mount({
+ components: {
+ 'd-carousel': Carousel,
+ 'd-carousel-item': CarouselItem,
+ 'd-button': Button,
+ },
+ template: `
+
+ {{ item }}
+
+
+ 上一张
+ 下一张
+ 第一张
+
+ `,
+ setup() {
+ const items = ref(["page 1", 'page 2', 'page 3', 'page 4'])
+ const activeIndex = ref(0)
+
+ const carousel = ref()
+
+ const onPrev = () => {
+ carousel.value?.prev?.()
+ }
+ const onNext = () => {
+ carousel.value?.next?.()
+ }
+ const onGoFirst = () => {
+ carousel.value?.goto?.(0)
+ }
+ const onChange = (index: number) => {
+ activeIndex.value = index
+ }
+
+ return {
+ activeIndex,
+ items,
+
+ carousel,
+ onPrev,
+ onNext,
+ onGoFirst,
+ onChange,
+ }
+ }
+ });
+
+ await nextTick()
+ wrapper.find('.arrow-left').trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(3)
+ wrapper.find('.arrow-right').trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(0)
+
+ wrapper.findAll('.devui-btn')[0].trigger('click')
+ await nextTick()
+ wrapper.findAll('.devui-btn')[0].trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(2)
+
+ wrapper.findAll('.devui-btn')[1].trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(3)
+
+ wrapper.findAll('.devui-btn')[2].trigger('click')
+ await nextTick()
+ expect(wrapper.vm.activeIndex).toBe(0)
+ });
+
+ it('autoplay', async () => {
+ const wrapper = mount({
+ components: {
+ 'd-carousel': Carousel,
+ 'd-carousel-item': CarouselItem,
+ },
+ template: `
+
+ Page 1
+ Page 2
+ Page 3
+ Page 4
+
+ `,
+ setup() {
+ const activeIndex = ref(2)
+
+ const onChange = (index: number) => {
+ activeIndex.value = index
+ }
+
+ return {
+ activeIndex,
+
+ onChange,
+ }
+ }
+ });
+
+ await wait(4500)
+ expect(wrapper.vm.activeIndex).toBe(1)
+ await wait(4600)
+ expect(wrapper.vm.activeIndex).toBe(3)
+ }, 10000);
+});
diff --git a/devui/carousel/carousel.tsx b/devui/carousel/carousel.tsx
index db977d77e97110723fbb146090ef18eb7219311a..db21aaeb04260d6383f1b756a00834cd4a2cc0a1 100644
--- a/devui/carousel/carousel.tsx
+++ b/devui/carousel/carousel.tsx
@@ -1,5 +1,5 @@
/* eslint-disable */
-import { defineComponent, ref, watch, onMounted, Fragment, Comment } from 'vue';
+import { defineComponent, ref, watch, onMounted, onBeforeUnmount, Fragment, Comment } from 'vue';
import { carouselProps, DotTrigger } from './types';
import Icon from '../icon/src/icon'
@@ -26,7 +26,7 @@ export default defineComponent({
const currentIndex = ref(0);
const wrapperRef = ref(null);
const containerRef = ref(null);
- const scheduledId = ref(null);
+ const scheduledId = ref | null>(null);
watch(
() => arrowTrigger,
@@ -170,6 +170,9 @@ export default defineComponent({
autoScheduleTransition();
});
+ onBeforeUnmount(() => {
+ clearScheduledTransition()
+ })
return {
wrapperRef,