Skip to content

Commit 94ef364

Browse files
ZhouY11BobbieGoede
andauthored
fix(component): preserve variant style on rerender (#203)
Co-authored-by: Bobbie Goede <[email protected]>
1 parent 54971c6 commit 94ef364

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

src/utils/component.ts

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
Variant,
1818
} from '../types/variants'
1919
import { useMotion } from '../useMotion'
20+
import { variantToStyle } from './transform'
2021

2122
/**
2223
* Type guard, checks if passed string is an existing preset
@@ -228,6 +229,18 @@ export function setupMotionComponent(
228229
)
229230
}
230231

232+
/**
233+
* Vue reapplies all styles every render, include style properties and calculated initially styles get reapplied every render.
234+
* To prevent this, reapply the current motion state styles in vnode updated lifecycle
235+
*/
236+
node.props.onVnodeUpdated = ({ el }) => {
237+
const styles = variantToStyle(instances[index].state as Variant)
238+
239+
for (const [key, val] of Object.entries(styles)) {
240+
(el as any).style[key] = val
241+
}
242+
}
243+
231244
return node
232245
}
233246

tests/components.spec.ts

+34-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { config, mount } from '@vue/test-utils'
22
import { describe, expect, it } from 'vitest'
3-
import { h, nextTick } from 'vue'
4-
import { MotionPlugin } from '../src'
3+
import { h, nextTick, ref } from 'vue'
4+
import { MotionComponent, MotionPlugin } from '../src'
55
import MotionGroup from '../src/components/MotionGroup'
66
import { intersect } from './utils/intersectionObserver'
77
import { getTestComponent, useCompletionFn, waitForMockCalls } from './utils'
@@ -10,8 +10,8 @@ import { getTestComponent, useCompletionFn, waitForMockCalls } from './utils'
1010
config.global.plugins.push(MotionPlugin)
1111

1212
describe.each([
13-
{ t: 'directive', name: '`v-motion` directive' },
14-
{ t: 'component', name: '`<Motion>` component' },
13+
{ t: 'directive', name: '`v-motion` directive (shared tests)' },
14+
{ t: 'component', name: '`<Motion>` component (shared tests)' },
1515
])(`$name`, async ({ t }) => {
1616
const TestComponent = getTestComponent(t)
1717

@@ -136,6 +136,36 @@ describe.each([
136136
})
137137
})
138138

139+
describe('`<Motion>` component', async () => {
140+
it('#202 - preserve variant style on rerender', async () => {
141+
const counter = ref(0)
142+
143+
const wrapper = mount(
144+
{ render: () => h(MotionComponent, null, () => counter.value) },
145+
{
146+
props: {
147+
initial: { scale: 1 },
148+
enter: { scale: 2 },
149+
duration: 10,
150+
},
151+
},
152+
)
153+
154+
const el = wrapper.element as HTMLDivElement
155+
await nextTick()
156+
157+
// Renders enter
158+
expect(el.style.transform).toEqual('scale(2) translateZ(0px)')
159+
160+
// Trigger rerender by updating slot variable
161+
counter.value++
162+
await nextTick()
163+
164+
// Variant style is preserved after rerender/update
165+
expect(el.style.transform).toEqual('scale(2) translateZ(0px)')
166+
})
167+
})
168+
139169
describe('`<MotionGroup>` component', async () => {
140170
it('child node can overwrite helpers', async () => {
141171
const wrapper = mount({

0 commit comments

Comments
 (0)