Skip to content

Commit 42d4b61

Browse files
committed
feat: stub transition and transition group
1 parent 9f343b0 commit 42d4b61

File tree

4 files changed

+84
-5
lines changed

4 files changed

+84
-5
lines changed

src/mount.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ export function mount(
353353
if (global.stubs || options?.shallow) {
354354
stubComponents(global.stubs, options?.shallow)
355355
} else {
356-
transformVNodeArgs()
356+
// still apply default stub of Transition and Transition Group
357+
stubComponents()
357358
}
358359

359360
// mount the app!

src/stubs.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
22
transformVNodeArgs,
3+
Transition,
4+
TransitionGroup,
35
h,
46
ComponentPublicInstance,
57
Slots,
@@ -34,6 +36,14 @@ const createStub = ({ name, props }: StubOptions): ComponentOptions => {
3436
return defineComponent({ name: name || anonName, render, props })
3537
}
3638

39+
const createTransitionStub = ({ props }: StubOptions): ComponentOptions => {
40+
const render = (ctx: ComponentPublicInstance) => {
41+
return h('transition-stub', {}, ctx.$slots)
42+
}
43+
44+
return defineComponent({ name: 'transition-stub', render, props })
45+
}
46+
3747
const resolveComponentStubByName = (
3848
componentName: string,
3949
stubs: Record<any, any>
@@ -82,6 +92,10 @@ export function stubComponents(
8292
// 1. a HTML tag (div, span...)
8393
// 2. An object of component options, such as { name: 'foo', render: [Function], props: {...} }
8494
// Depending what it is, we do different things.
95+
if (type === Transition || type === TransitionGroup) {
96+
return [createTransitionStub({ props: undefined }), undefined, children]
97+
}
98+
8599
if (
86100
isHTMLElement(type) ||
87101
isCommentOrFragment(type) ||

src/utils/isElementVisible.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
* Licensed under the MIT License.
55
*/
66

7-
type StylableElement = HTMLElement | SVGElement
7+
function isStyleVisible<T extends Element>(element: T) {
8+
if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) {
9+
return false
10+
}
811

9-
function isStyleVisible<T extends StylableElement>(element: T) {
1012
const { display, visibility, opacity } = element.style
13+
1114
return (
1215
display !== 'none' &&
1316
visibility !== 'hidden' &&
@@ -16,14 +19,14 @@ function isStyleVisible<T extends StylableElement>(element: T) {
1619
)
1720
}
1821

19-
function isAttributeVisible<T extends StylableElement>(element: T) {
22+
function isAttributeVisible<T extends Element>(element: T) {
2023
return (
2124
!element.hasAttribute('hidden') &&
2225
(element.nodeName === 'DETAILS' ? element.hasAttribute('open') : true)
2326
)
2427
}
2528

26-
export function isElementVisible<T extends StylableElement>(element: T) {
29+
export function isElementVisible<T extends Element>(element: T) {
2730
return (
2831
element.nodeName !== '#comment' &&
2932
isStyleVisible(element) &&

tests/isVisible.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,65 @@ describe('isVisible', () => {
4545
await wrapper.find('button').trigger('click')
4646
expect(wrapper.find('span').isVisible()).toBe(false)
4747
})
48+
49+
it('handles transitions', async () => {
50+
const Comp = {
51+
template: `
52+
<button @click="show = false" />
53+
<transition name="fade">
54+
<span class="item" v-show="show">
55+
Content
56+
</span>
57+
</transition>
58+
`,
59+
data() {
60+
return {
61+
show: true
62+
}
63+
}
64+
}
65+
const wrapper = mount(Comp, {})
66+
67+
expect(wrapper.find('span').isVisible()).toBe(true)
68+
await wrapper.find('button').trigger('click')
69+
expect(wrapper.find('span').isVisible()).toBe(false)
70+
})
71+
72+
it('handles transition-group', async () => {
73+
const Comp = {
74+
template: `
75+
<div id="list-demo">
76+
<button @click="add" id="add">Add</button>
77+
<button @click="remove" id="remove">Remove</button>
78+
<transition-group name="list" tag="p">
79+
<span v-for="item in items" :key="item" class="list-item">
80+
Item: {{ item }}
81+
</span>
82+
</transition-group>
83+
</div>
84+
`,
85+
methods: {
86+
add() {
87+
this.items.push(2)
88+
},
89+
remove() {
90+
this.items.splice(1) // back to [1]
91+
}
92+
},
93+
data() {
94+
return {
95+
items: [1]
96+
}
97+
}
98+
}
99+
const wrapper = mount(Comp)
100+
101+
expect(wrapper.html()).toContain('Item: 1')
102+
await wrapper.find('#add').trigger('click')
103+
expect(wrapper.html()).toContain('Item: 1')
104+
expect(wrapper.html()).toContain('Item: 2')
105+
await wrapper.find('#remove').trigger('click')
106+
expect(wrapper.html()).toContain('Item: 1')
107+
expect(wrapper.html()).not.toContain('Item: 2')
108+
})
48109
})

0 commit comments

Comments
 (0)