From 94ef3645bb32da0aac8306242c64ee06c48cd1ac Mon Sep 17 00:00:00 2001 From: ZhouY11 <79828139+ZhouY11@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:27:17 +0800 Subject: [PATCH] fix(component): preserve variant style on rerender (#203) Co-authored-by: Bobbie Goede <bobbiegoede@gmail.com> --- src/utils/component.ts | 13 +++++++++++++ tests/components.spec.ts | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/utils/component.ts b/src/utils/component.ts index 90268855..5a67496b 100644 --- a/src/utils/component.ts +++ b/src/utils/component.ts @@ -17,6 +17,7 @@ import type { Variant, } from '../types/variants' import { useMotion } from '../useMotion' +import { variantToStyle } from './transform' /** * Type guard, checks if passed string is an existing preset @@ -228,6 +229,18 @@ export function setupMotionComponent( ) } + /** + * Vue reapplies all styles every render, include style properties and calculated initially styles get reapplied every render. + * To prevent this, reapply the current motion state styles in vnode updated lifecycle + */ + node.props.onVnodeUpdated = ({ el }) => { + const styles = variantToStyle(instances[index].state as Variant) + + for (const [key, val] of Object.entries(styles)) { + (el as any).style[key] = val + } + } + return node } diff --git a/tests/components.spec.ts b/tests/components.spec.ts index bd856353..70095238 100644 --- a/tests/components.spec.ts +++ b/tests/components.spec.ts @@ -1,7 +1,7 @@ import { config, mount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' -import { h, nextTick } from 'vue' -import { MotionPlugin } from '../src' +import { h, nextTick, ref } from 'vue' +import { MotionComponent, MotionPlugin } from '../src' import MotionGroup from '../src/components/MotionGroup' import { intersect } from './utils/intersectionObserver' import { getTestComponent, useCompletionFn, waitForMockCalls } from './utils' @@ -10,8 +10,8 @@ import { getTestComponent, useCompletionFn, waitForMockCalls } from './utils' config.global.plugins.push(MotionPlugin) describe.each([ - { t: 'directive', name: '`v-motion` directive' }, - { t: 'component', name: '`<Motion>` component' }, + { t: 'directive', name: '`v-motion` directive (shared tests)' }, + { t: 'component', name: '`<Motion>` component (shared tests)' }, ])(`$name`, async ({ t }) => { const TestComponent = getTestComponent(t) @@ -136,6 +136,36 @@ describe.each([ }) }) +describe('`<Motion>` component', async () => { + it('#202 - preserve variant style on rerender', async () => { + const counter = ref(0) + + const wrapper = mount( + { render: () => h(MotionComponent, null, () => counter.value) }, + { + props: { + initial: { scale: 1 }, + enter: { scale: 2 }, + duration: 10, + }, + }, + ) + + const el = wrapper.element as HTMLDivElement + await nextTick() + + // Renders enter + expect(el.style.transform).toEqual('scale(2) translateZ(0px)') + + // Trigger rerender by updating slot variable + counter.value++ + await nextTick() + + // Variant style is preserved after rerender/update + expect(el.style.transform).toEqual('scale(2) translateZ(0px)') + }) +}) + describe('`<MotionGroup>` component', async () => { it('child node can overwrite helpers', async () => { const wrapper = mount({