diff --git a/components/Calendar/__test__/__snapshots__/calendar.spec.ts.snap b/components/Calendar/__test__/__snapshots__/calendar.spec.ts.snap index ea4e53a1..8a50640b 100644 --- a/components/Calendar/__test__/__snapshots__/calendar.spec.ts.snap +++ b/components/Calendar/__test__/__snapshots__/calendar.spec.ts.snap @@ -35,34 +35,10 @@ exports[`Test: KCalendar > events: select 3`] = ` } `; -exports[`Test: KCalendar > props: cls 1`] = `"
SunMonTueWedThuFriSat
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
"`; - -exports[`Test: KCalendar > props: fullscreen is false and mode is month 1`] = `"
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
"`; - -exports[`Test: KCalendar > props: fullscreen is false and mode is year 1`] = `"
SunMonTueWedThuFriSat
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
"`; - -exports[`Test: KCalendar > props: locale 1`] = `"
周一周二周三周四周五周六周日
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
"`; - -exports[`Test: KCalendar > props: locale 2`] = `"
一月
二月
三月
四月
五月
六月
七月
八月
九月
十月
十一月
十二月
"`; - exports[`Test: KCalendar > props: mode 1`] = `"
Apr
"`; exports[`Test: KCalendar > props: mode 2`] = `"
25
"`; -exports[`Test: KCalendar > props: range 1`] = ` -"
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
" -`; - -exports[`Test: KCalendar > props: range 2`] = ` -"
SunMonTueWedThuFriSat
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
" -`; - exports[`Test: KCalendar > props: value 1`] = `"
25
"`; exports[`Test: KCalendar > slots: dateCell 1`] = `"
25
25
"`; diff --git a/components/Carousel/__test__/__snapshots__/carousel.spec.ts.snap b/components/Carousel/__test__/__snapshots__/carousel.spec.ts.snap index b6108463..6e9db913 100644 --- a/components/Carousel/__test__/__snapshots__/carousel.spec.ts.snap +++ b/components/Carousel/__test__/__snapshots__/carousel.spec.ts.snap @@ -24,4 +24,4 @@ exports[`Test: KCarousel > props: cls 1`] = `""`; -exports[`Test: KCarousel > slot: custom arrow indicators 1`] = `""`; +exports[`Test: KCarousel > slot: custom arrow indicators 1`] = `""`; diff --git a/components/Carousel/__test__/fixture/custom.svelte b/components/Carousel/__test__/fixture/custom.svelte index 693c40a0..c7492860 100644 --- a/components/Carousel/__test__/fixture/custom.svelte +++ b/components/Carousel/__test__/fixture/custom.svelte @@ -1,7 +1,7 @@ + + + + + diff --git a/components/Tour/__test__/fixture/slots.btn.svelte b/components/Tour/__test__/fixture/slots.btn.svelte new file mode 100644 index 00000000..0a32feaf --- /dev/null +++ b/components/Tour/__test__/fixture/slots.btn.svelte @@ -0,0 +1,28 @@ + + + + + + +
+ + +
diff --git a/components/Tour/__test__/fixture/slots.close.svelte b/components/Tour/__test__/fixture/slots.close.svelte new file mode 100644 index 00000000..167807a9 --- /dev/null +++ b/components/Tour/__test__/fixture/slots.close.svelte @@ -0,0 +1,29 @@ + + + + + +
+ + +
diff --git a/components/Tour/__test__/fixture/slots.descr.svelte b/components/Tour/__test__/fixture/slots.descr.svelte new file mode 100644 index 00000000..659d4974 --- /dev/null +++ b/components/Tour/__test__/fixture/slots.descr.svelte @@ -0,0 +1,30 @@ + + + +
+ {current}-{description} +
+
+
+ + +
diff --git a/components/Tour/__test__/fixture/slots.footer.svelte b/components/Tour/__test__/fixture/slots.footer.svelte new file mode 100644 index 00000000..b556e9e6 --- /dev/null +++ b/components/Tour/__test__/fixture/slots.footer.svelte @@ -0,0 +1,30 @@ + + + +
+ + +
+
+
+ + +
diff --git a/components/Tour/__test__/fixture/slots.indicator.svelte b/components/Tour/__test__/fixture/slots.indicator.svelte new file mode 100644 index 00000000..edb06e88 --- /dev/null +++ b/components/Tour/__test__/fixture/slots.indicator.svelte @@ -0,0 +1,29 @@ + + + +
+ indicators-{current} +
+
+
+ + +
diff --git a/components/Tour/__test__/fixture/slots.title.svelte b/components/Tour/__test__/fixture/slots.title.svelte new file mode 100644 index 00000000..52fb73f5 --- /dev/null +++ b/components/Tour/__test__/fixture/slots.title.svelte @@ -0,0 +1,29 @@ + + + +
+ {current}-{title} +
+
+
+ + +
diff --git a/components/Tour/__test__/tour.spec.ts b/components/Tour/__test__/tour.spec.ts index 420c3d03..971ceeb2 100644 --- a/components/Tour/__test__/tour.spec.ts +++ b/components/Tour/__test__/tour.spec.ts @@ -1,10 +1,27 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import KTour from '../src'; +import KTourSlotsTitle from './fixture/slots.title.svelte'; +import KTourSlotsDescr from './fixture/slots.descr.svelte'; +import KTourSlotsClose from './fixture/slots.close.svelte'; +import KTourSlotsIndicators from './fixture/slots.indicator.svelte'; +import KTourSlotsFooter from './fixture/slots.footer.svelte'; +import KTourSlotsBtn from './fixture/slots.btn.svelte'; +import KTourOpen from './fixture/open.svelte'; +import { tick } from 'svelte'; let host; const initHost = () => { host = globalThis.document.createElement('div'); + const target = globalThis.document.createElement('div'); + target.id = 'ikun-ui-tour'; + const target2 = globalThis.document.createElement('div'); + target2.id = 'ikun-ui-tour2'; + const target3 = globalThis.document.createElement('div'); + target3.id = 'ikun-ui-tour3'; + host.appendChild(target); + host.appendChild(target2); + host.appendChild(target3); host.setAttribute('id', 'host'); globalThis.document.body.appendChild(host); }; @@ -18,15 +35,364 @@ afterEach(() => { }); describe('Test: KTour', () => { + vi.mock('svelte', async () => { + const actual = (await vi.importActual('svelte')) as object; + return { + ...actual, + // @ts-ignore + onMount: (await import('svelte/internal')).onMount + }; + }); + test('props: cls', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore const instance = new KTour({ target: host, props: { - cls: 'k-tour--test' + cls: 'k-tour--test', + open: true, + steps: [{ target }] } }); expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); expect(host!.innerHTML.includes('k-tour--test')).toBeTruthy(); expect(host.innerHTML).matchSnapshot(); }); + + test('props: open', async () => { + // @ts-ignore + const instance = new KTourOpen({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + const trigger = document.querySelector('#bwsy_trigger'); + (trigger as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: mask', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: prevBtnText & nextBtnText', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + prevBtnText: '上一步', + nextBtnText: '下一步', + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const btns = document.querySelectorAll('.k-button'); + expect(btns[0].innerHTML.includes('上一步')).toBeTruthy(); + expect(btns[1].innerHTML.includes('下一步')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: closeIcon', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + closeIcon: 'i-custom-icon', + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const close = document.querySelector('.k-tour-close'); + expect(close!.innerHTML.includes('i-custom-icon')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: current', async () => { + const target = document.querySelector('#ikun-ui-tour'); + const target2 = document.querySelector('#ikun-ui-tour2'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + current: 1, + steps: [{ target }, { target: target2 }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: zIndex', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + zIndex: 2077, + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const ktEl = document.querySelector('.k-tour'); + expect((ktEl as HTMLElement)!.style.zIndex).toBe('2077'); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: steps', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + steps: [ + { + target, + title: 'ikun-ui title', + description: 'ikun-ui description' + } + ] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML.includes('ikun-ui title')).toBeTruthy(); + expect(host.innerHTML.includes('ikun-ui description')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: zIndex', async () => { + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + zIndex: 2077, + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const ktEl = document.querySelector('.k-tour'); + expect((ktEl as HTMLElement)!.style.zIndex).toBe('2077'); + expect(host.innerHTML).matchSnapshot(); + }); + + test('events: close', async () => { + const mockFn = vi.fn(); + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + // @ts-ignore + instance.$on('close', () => { + mockFn(); + }); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const close = document.querySelector('.k-tour-close'); + (close as HTMLButtonElement)?.click(); + await tick(); + expect(mockFn).toBeCalled(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('events: finish', async () => { + const mockFn = vi.fn(); + const target = document.querySelector('#ikun-ui-tour'); + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + open: true, + mask: false, + steps: [{ target }] + } + }); + expect(instance).toBeTruthy(); + // @ts-ignore + instance.$on('finish', () => { + mockFn(); + }); + await tick(); + await vi.advanceTimersByTimeAsync(300); + + const next = document.querySelector('.k-button--primary__fill'); + (next as HTMLButtonElement)?.click(); + await tick(); + expect(mockFn).toBeCalled(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('events: change', async () => { + const mockFn = vi.fn(); + const target = document.querySelector('#ikun-ui-tour'); + const target2 = document.querySelector('#ikun-ui-tour2'); + let current = 0; + // @ts-ignore + const instance = new KTour({ + target: host, + props: { + current, + open: true, + mask: false, + steps: [{ target }, { target: target2 }] + } + }); + expect(instance).toBeTruthy(); + // @ts-ignore + instance.$on('change', (e) => { + current = e.detail; + mockFn(); + }); + await tick(); + await vi.advanceTimersByTimeAsync(300); + + const next = document.querySelector('.k-button--primary__fill'); + (next as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(mockFn).toBeCalled(); + expect(current).toBe(1); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: title', async () => { + // @ts-ignore + const instance = new KTourSlotsTitle({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: description', async () => { + // @ts-ignore + const instance = new KTourSlotsDescr({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: closeIcon', async () => { + // @ts-ignore + const instance = new KTourSlotsClose({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const close = document.querySelector('#bwsy'); + expect(host.innerHTML.includes('custom close')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + (close as HTMLButtonElement)?.click(); + await tick(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: indicators', async () => { + // @ts-ignore + const instance = new KTourSlotsIndicators({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: footer', async () => { + // @ts-ignore + const instance = new KTourSlotsFooter({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + const prev = document.querySelector('#bwsy_prev'); + (prev as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + const next = document.querySelector('#bwsy_next'); + (next as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slots: nextButton & prevButton', async () => { + // @ts-ignore + const instance = new KTourSlotsBtn({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + const prev = document.querySelector('#bwsy_prev'); + (prev as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + const next = document.querySelector('#bwsy_next'); + (next as HTMLButtonElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); }); diff --git a/components/Tour/src/index.svelte b/components/Tour/src/index.svelte index 22368ce1..8d41d5a9 100644 --- a/components/Tour/src/index.svelte +++ b/components/Tour/src/index.svelte @@ -97,7 +97,9 @@ `0`; maskHeight = maskWidth = `100%`; maskHeight = `100%`; - maskRootBg = 'rgba(0,0,0,0.5)'; + if (mask) { + maskRootBg = 'rgba(0,0,0,0.5)'; + } } } if (el && target) { @@ -107,27 +109,31 @@ setFullScreen(); return; } + el.style.width = `${width}px`; el.style.height = `${height}px`; el.style.position = 'fixed'; el.style.top = `${top}px`; el.style.left = `${left}px`; + el.style.transform = `translate(0, 0)`; maskRootTransition = transition ? 'border-width .3s' : ''; maskWidth = `${width}px`; maskHeight = `${height}px`; + if (mask) { + maskBorderTopWidth = `${Math.max(top, 0)}px`; + maskBorderLeftWidth = `${Math.max(left, 0)}px`; + const { marginRight, marginLeft, marginBottom, marginTop } = window.getComputedStyle( + document.body + ); + const widthWithMargin = + document.body.offsetWidth + parseInt(marginLeft) + parseInt(marginRight); + const heightWithMargin = + document.body.offsetHeight + parseInt(marginBottom) + parseInt(marginTop); + maskBorderBottomWidth = `${Math.max(heightWithMargin - height - top, 0)}px`; + maskBorderRightWidth = `${Math.max(widthWithMargin - width - left, 0)}px`; + } - maskBorderTopWidth = `${Math.max(top, 0)}px`; - maskBorderLeftWidth = `${Math.max(left, 0)}px`; - const { marginRight, marginLeft, marginBottom, marginTop } = window.getComputedStyle( - document.body - ); - const widthWithMargin = - document.body.offsetWidth + parseInt(marginLeft) + parseInt(marginRight); - const heightWithMargin = - document.body.offsetHeight + parseInt(marginBottom) + parseInt(marginTop); - maskBorderBottomWidth = `${Math.max(heightWithMargin - height - top, 0)}px`; - maskBorderRightWidth = `${Math.max(widthWithMargin - width - left, 0)}px`; maskRootBg = 'transparent'; } else if (el && !target) { setFullScreen(); @@ -220,10 +226,10 @@ style:transition={maskRootTransition} style:z-index={zIndex} > - +
- + {#if steps[index].title} {steps[index].title} {/if} @@ -237,7 +243,7 @@
- + {#if steps[index].description} {steps[index].description} {/if} diff --git a/components/Tour/src/types.d.ts b/components/Tour/src/types.d.ts index abcce18a..7e79749f 100644 --- a/components/Tour/src/types.d.ts +++ b/components/Tour/src/types.d.ts @@ -49,29 +49,3 @@ export interface KTourStepsOption { title?: string; description?: string; } -// TODO: ☑️KTour props prevBtnText 上一步按钮文本 -// TODO: ☑️KTour props nextBtnText 下一步按钮文本 -// TODO: ☑️KTour props mask 是否启用遮罩 -// TODO: ☑️KTour props closeIcon 自定义关闭按钮 -// TODO: ✅KTour props placement 引导卡片相对于目标元素的位置 -// TODO: ☑️KTour props open 打开引导 -// TODO: ☑️KTour props current 当前处于哪一步(number default) -// TODO: ☑️️KTour props scrollIntoViewOptions 是否支持当前元素(引导元素)滚动到视窗内,也可传入配置指定滚动视窗的相关参数 -// TODO: ☑️ KTour props zIndex Tour 的层级 - -// TODO: ☑️KTour events onClose 关闭引导时的回调函数 -// TODO: ☑️KTour events onFinish 引导完成时的回调 -// TODO: ☑️KTour events onChange 步骤改变时的回调,current 为当前的步骤,(current: number) => void - -// TODO: ☑️KTour slots closeIcon 自定义关闭按钮插槽 - -// TODO: ☑️ KTour props step step.target 获取引导卡片指向的元素,为空时居中于屏幕 -// TODO: ☑️ KTour props step step.title 标题 -// TODO: ☑️ KTour props step step.description 标题 - -// TODO: ☑️ KTour slots indicators 自定义指示器 -// TODO: ☑️ KTour slots title 标题 -// TODO: ☑️ KTour slots description 主要描述部分 -// TODO: ☑️ KTour slots footer 主要描述部分 -// TODO: ☑️ KTour slots nextButton 自定义下一步按钮 -// TODO: ☑️ KTour slots prevButton 自定义上一步按钮