diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 81a084a..b742c3c 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix focus styles showing up when using the mouse ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347)) - Fix "Can't perform a React state update on an unmounted component." when using the `Transition` component ([#2374](https://github.com/tailwindlabs/headlessui/pull/2374)) - Add `FocusTrap` event listeners once document has loaded ([#2389](https://github.com/tailwindlabs/headlessui/pull/2389)) +- Fix `className` hydration for `` ([#2390](https://github.com/tailwindlabs/headlessui/pull/2390)) ### Added diff --git a/packages/@headlessui-react/src/components/transitions/transition.ssr.test.tsx b/packages/@headlessui-react/src/components/transitions/transition.ssr.test.tsx index 64a6108..2648043 100644 --- a/packages/@headlessui-react/src/components/transitions/transition.ssr.test.tsx +++ b/packages/@headlessui-react/src/components/transitions/transition.ssr.test.tsx @@ -9,8 +9,34 @@ beforeAll(() => { describe('Rendering', () => { describe('SSR', () => { + it('A transition without appear=true does not insert classes during SSR', async () => { + let result = await renderSSR( + +
+
+ ) + + let div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner') + + await result.hydrate() + + div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner') + }) + it('should not overwrite className of children when as=Fragment', async () => { - await renderSSR( + let result = await renderSSR( { expect(div).not.toBeNull() expect(div?.className).toBe('inner enter enter-from') + + await result.hydrate() + + div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner enter enter-from') }) }) }) diff --git a/packages/@headlessui-react/src/components/transitions/transition.tsx b/packages/@headlessui-react/src/components/transitions/transition.tsx index fe20bf6..9925609 100644 --- a/packages/@headlessui-react/src/components/transitions/transition.tsx +++ b/packages/@headlessui-react/src/components/transitions/transition.tsx @@ -436,7 +436,7 @@ function TransitionChildFn { describe('Rendering', () => { describe('SSR', () => { + it('A transition without appear=true does not insert classes during SSR', async () => { + let result = await renderSSR( + defineComponent({ + components: Transition, + template: html` + +
+
+ `, + }) + ) + + let div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner') + + // If we don't await then we get the same for SSR and hydration + // but we want to investigate what effects this has on our other transition tests too + await result.hydrate() + + div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner enter enter-from') + }) + it('should not overwrite className of children when as=Fragment', async () => { - await renderSSR( + let result = await renderSSR( defineComponent({ components: Transition, template: html` @@ -33,6 +66,13 @@ describe('Rendering', () => { expect(div).not.toBeNull() expect(div?.className).toBe('inner enter enter-from') + + await result.hydrate() + + div = document.querySelector('.inner') + + expect(div).not.toBeNull() + expect(div?.className).toBe('inner enter enter-from') }) }) }) diff --git a/packages/@headlessui-vue/src/components/transitions/transition.ts b/packages/@headlessui-vue/src/components/transitions/transition.ts index 64e2e88..89dcd74 100644 --- a/packages/@headlessui-vue/src/components/transitions/transition.ts +++ b/packages/@headlessui-vue/src/components/transitions/transition.ts @@ -354,7 +354,7 @@ export let TransitionChild = defineComponent({ let ourProps = { ref: container } let theirProps = { ...rest, - ...(appear && show && env.isServer + ...(appear.value && show.value && env.isServer ? { // Already apply the `enter` and `enterFrom` on the server if required class: normalizeClass([