Change default tag from div to Fragment on Transition components (#3110)

* use `Fragment` as the default element for `Transition` components

* update tests to reflect default tag change

* only error on missing `ref` if it's actually required

If the `<Transition />` component "root" is used as a root placeholder
(for state management) and not making actual transitions itself, then we
don't require a `ref` element.

* add test to ensure we don't error on missing `ref` when not required

+ add `className="…"` to some places to indicate that we _do_ want to
  perform a transition and thus have to fail if the `ref` is missing.

* improve `requiresRef` check

Also ensure that a ref is required if the `as` prop is provided and
it's not a `Fragment`

* add `shouldForwardRef` helper

* fix broken tests

These tests were rendering a `Debug` element that didn't render any DOM
nodes. Adding `as="div"` ensures that we are forwarding the ref
correctly.

* update changelog

* update playgrounds to reflect tag change

* Tweak changelog

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
This commit is contained in:
Robin Malfait
2024-04-19 16:15:11 +02:00
committed by GitHub
parent 83cda0aa75
commit 8fa5caf0dc
22 changed files with 182 additions and 85 deletions
+1
View File
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use native `useId` and `useSyncExternalStore` hooks ([#3092](https://github.com/tailwindlabs/headlessui/pull/3092))
- Use `absolute` as the default Floating UI strategy ([#3097](https://github.com/tailwindlabs/headlessui/pull/3097))
- Change default tags for `ListboxOptions`, `ListboxOption`, `ComboboxOptions`, `ComboboxOption` and `TabGroup` components ([#3109](https://github.com/tailwindlabs/headlessui/pull/3109))
- Change default tag from `div` to `Fragment` on `Transition` components ([#3110](https://github.com/tailwindlabs/headlessui/pull/3110))
### Added
@@ -597,7 +597,7 @@ describe('Composition', () => {
<Transition>
<Debug name="Transition" fn={orderFn} />
<Disclosure.Panel>
<Transition.Child>
<Transition.Child as="div">
<Debug name="Transition.Child" fn={orderFn} />
</Transition.Child>
</Disclosure.Panel>
@@ -977,7 +977,7 @@ describe('Composition', () => {
<Transition>
<Debug name="Transition" fn={orderFn} />
<Popover.Panel>
<Transition.Child>
<Transition.Child as="div">
<Debug name="Transition.Child" fn={orderFn} />
</Transition.Child>
</Popover.Panel>
@@ -111,12 +111,6 @@ exports[`Setup API shallow should passthrough all the props (that we do not use
</a>
`;
exports[`Setup API shallow should render a div and its children by default 1`] = `
<div>
Children
</div>
`;
exports[`Setup API shallow should render another component if the \`as\` prop is used and its children by default 1`] = `
<a>
Children
@@ -1,5 +1,5 @@
import { act as _act, fireEvent, render } from '@testing-library/react'
import React, { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { getByText } from '../../test-utils/accessibility-assertions'
import { executeTimeline } from '../../test-utils/execute-timeline'
import { click } from '../../test-utils/interactions'
@@ -22,7 +22,7 @@ function nextFrame() {
it('should not steal the ref from the child', async () => {
let fn = jest.fn()
render(
<Transition show={true} as={Fragment}>
<Transition show={true}>
<div ref={fn}>...</div>
</Transition>
)
@@ -40,11 +40,6 @@ it('should render without crashing', () => {
)
})
it('should be possible to render a Transition without children', () => {
render(<Transition show={true} className="transition" />)
expect(document.getElementsByClassName('transition')).not.toBeNull()
})
it(
'should yell at us when we forget the required show prop',
suppressConsoleLogs(() => {
@@ -62,16 +57,15 @@ it(
describe('Setup API', () => {
describe('shallow', () => {
it('should render a div and its children by default', () => {
let { container } = render(<Transition show={true}>Children</Transition>)
expect(container.firstChild).toMatchSnapshot()
})
it('should passthrough all the props (that we do not use internally)', () => {
let { container } = render(
/**
* Renders a Fragment by default and forwards props. But not possible to
* type in TypeScript land. This is also discouraged, but it works.
*/
// @ts-expect-error
<Transition show={true} id="root" className="text-blue-400">
Children
<div>Children</div>
</Transition>
)
@@ -99,7 +93,11 @@ describe('Setup API', () => {
})
it('should render nothing when the show prop is false', () => {
let { container } = render(<Transition show={false}>Children</Transition>)
let { container } = render(
<Transition show={false}>
<div>Children</div>
</Transition>
)
expect(container.firstChild).toMatchSnapshot()
})
@@ -115,11 +113,7 @@ describe('Setup API', () => {
})
it('should be possible to use a render prop', () => {
let { container } = render(
<Transition show={true} as={Fragment}>
{() => <span>Children</span>}
</Transition>
)
let { container } = render(<Transition show={true}>{() => <span>Children</span>}</Transition>)
expect(container.firstChild).toMatchSnapshot()
})
@@ -135,7 +129,7 @@ describe('Setup API', () => {
expect(() => {
render(
<Transition show={true} as={Fragment}>
<Transition show={true} enter="duration-200">
{() => <Dummy />}
</Transition>
)
@@ -153,7 +147,9 @@ describe('Setup API', () => {
expect(() => {
render(
<div className="My Page">
<Transition.Child>Oops</Transition.Child>
<Transition.Child>
<div>Oops</div>
</Transition.Child>
</div>
)
}).toThrowErrorMatchingSnapshot()
@@ -163,7 +159,9 @@ describe('Setup API', () => {
it('should be possible to render a Transition.Child without children', () => {
render(
<Transition show={true}>
<Transition.Child className="transition" />
<Transition.Child>
<div className="transition" />
</Transition.Child>
</Transition>
)
expect(document.getElementsByClassName('transition')).not.toBeNull()
@@ -172,7 +170,9 @@ describe('Setup API', () => {
it('should be possible to use a Transition.Root and a Transition.Child', () => {
render(
<Transition.Root show={true}>
<Transition.Child className="transition" />
<Transition.Child>
<div className="transition" />
</Transition.Child>
</Transition.Root>
)
expect(document.getElementsByClassName('transition')).not.toBeNull()
@@ -182,8 +182,14 @@ describe('Setup API', () => {
let { container } = render(
<div className="My Page">
<Transition show={true}>
<Transition.Child>Sidebar</Transition.Child>
<Transition.Child>Content</Transition.Child>
<div>
<Transition.Child>
<div>Sidebar</div>
</Transition.Child>
<Transition.Child>
<div>Content</div>
</Transition.Child>
</div>
</Transition>
</div>
)
@@ -195,8 +201,10 @@ describe('Setup API', () => {
let { container } = render(
<div className="My Page">
<Transition show={true}>
<Transition.Child as="aside">Sidebar</Transition.Child>
<Transition.Child as="section">Content</Transition.Child>
<div>
<Transition.Child as="aside">Sidebar</Transition.Child>
<Transition.Child as="section">Content</Transition.Child>
</div>
</Transition>
</div>
)
@@ -221,8 +229,10 @@ describe('Setup API', () => {
let { container } = render(
<div className="My Page">
<Transition show={true}>
<Transition.Child as={Fragment}>{() => <aside>Sidebar</aside>}</Transition.Child>
<Transition.Child as={Fragment}>{() => <section>Content</section>}</Transition.Child>
<div>
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
<Transition.Child>{() => <section>Content</section>}</Transition.Child>
</div>
</Transition>
</div>
)
@@ -233,13 +243,11 @@ describe('Setup API', () => {
it('should be possible to use render props on the Transition and Transition.Child components', () => {
let { container } = render(
<div className="My Page">
<Transition show={true} as={Fragment}>
<Transition show={true}>
{() => (
<article>
<Transition.Child as={Fragment}>{() => <aside>Sidebar</aside>}</Transition.Child>
<Transition.Child as={Fragment}>
{() => <section>Content</section>}
</Transition.Child>
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
<Transition.Child>{() => <section>Content</section>}</Transition.Child>
</article>
)}
</Transition>
@@ -262,8 +270,14 @@ describe('Setup API', () => {
render(
<div className="My Page">
<Transition show={true}>
<Transition.Child as={Fragment}>{() => <Dummy>Sidebar</Dummy>}</Transition.Child>
<Transition.Child as={Fragment}>{() => <Dummy>Content</Dummy>}</Transition.Child>
<div>
<Transition.Child enter="duration-200">
{() => <Dummy>Sidebar</Dummy>}
</Transition.Child>
<Transition.Child enter="duration-200">
{() => <Dummy>Content</Dummy>}
</Transition.Child>
</div>
</Transition>
</div>
)
@@ -271,6 +285,28 @@ describe('Setup API', () => {
})
)
it(
'should not error when the `Transition` component does not contain any props that need to be forwarded',
suppressConsoleLogs(() => {
expect.assertions(1)
expect(() => {
render(
<div className="My Page">
<Transition show={true}>
<Transition.Child>
<div>Sidebar</div>
</Transition.Child>
<Transition.Child>
<div>Content</div>
</Transition.Child>
</Transition>
</div>
)
}).not.toThrow()
})
)
it(
'should yell at us when we forgot to forward a ref on the Transition component',
suppressConsoleLogs(() => {
@@ -283,7 +319,7 @@ describe('Setup API', () => {
expect(() => {
render(
<div className="My Page">
<Transition show={true} as={Fragment}>
<Transition show={true} enter="duration-200">
{() => (
<Dummy>
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
@@ -362,7 +398,7 @@ describe('Setup API', () => {
leaveFrom="leave-from"
leaveTo="leave-to"
>
Children
<div>Children</div>
</Transition>
)
@@ -387,7 +423,7 @@ describe('Setup API', () => {
leaveFrom="leave-from"
leaveTo="leave-to"
>
Children
<div>Children</div>
</Transition>
)
}
@@ -732,10 +768,10 @@ describe('Transitions', () => {
<Transition show={show}>
<Transition.Child leave="leave-fast" leaveFrom="leave-from" leaveTo="leave-to">
I am fast
<div>I am fast</div>
</Transition.Child>
<Transition.Child leave="leave-slow" leaveFrom="leave-from" leaveTo="leave-to">
I am slow
<div>I am slow</div>
</Transition.Child>
</Transition>
@@ -781,11 +817,11 @@ describe('Transitions', () => {
<Transition.Child leave="leave-fast" leaveFrom="leave-from" leaveTo="leave-to">
<span>I am fast</span>
<Transition show={show} leave="leave-slow">
I am my own root component and I don't talk to the parent
<div>I am my own root component and I don't talk to the parent</div>
</Transition>
</Transition.Child>
<Transition.Child leave="leave-slow" leaveFrom="leave-from" leaveTo="leave-to">
I am slow
<div>I am slow</div>
</Transition.Child>
</Transition>
@@ -953,12 +989,22 @@ describe('Events', () => {
<style>{`.child-2-2.leave { transition-duration: ${leaveDuration * 2.5}ms; }`}</style>
<Transition show={show} {...getProps('root')}>
<Transition.Child {...getProps('child-1')}>Child 1.</Transition.Child>
<Transition.Child {...getProps('child-2')}>
Child 2.
<Transition.Child {...getProps('child-2-1')}>Child 2.1.</Transition.Child>
<Transition.Child {...getProps('child-2-2')}>Child 2.2.</Transition.Child>
</Transition.Child>
<div>
<Transition.Child {...getProps('child-1')}>
<div>Child 1.</div>
</Transition.Child>
<Transition.Child {...getProps('child-2')}>
<div>
<div>Child 2.</div>
<Transition.Child {...getProps('child-2-1')}>
<div>Child 2.1.</div>
</Transition.Child>
<Transition.Child {...getProps('child-2-2')}>
<div>Child 2.2.</div>
</Transition.Child>
</div>
</Transition.Child>
</div>
</Transition>
<button
@@ -1104,7 +1150,9 @@ describe('Events', () => {
leave="leave-1"
leaveFrom="leave-from"
leaveTo="leave-to"
/>
>
<div />
</Transition.Child>
<Transition.Child
enter="enter-1"
enterFrom="enter-from"
@@ -49,6 +49,44 @@ function splitClasses(classes: string = '') {
return classes.split(/\s+/).filter((className) => className.length > 1)
}
/**
* Check if we should forward the ref to the child element or not. This is to
* prevent crashes when the `as` prop is a Fragment _and_ the component just acts
* as a state container (aka, there is no actual transition happening).
*
* E.g.:
*
* ```tsx
* <Transition show={true}>
* <Transition.Child enter="duration-100"><div>Child 1</div></Transition.Child>
* <Transition.Child enter="duration-200"><div>Child 2</div></Transition.Child>
* </Transition>
* ```
*
* In this scenario, the child components are transitioning, but the
* `Transition` parent, which is a `Fragment`, is not. So we should not forward
* the ref to the `Fragment`.
*/
function shouldForwardRef<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(
props: TransitionRootProps<TTag>
) {
return (
// If we have any of the enter/leave classes
Boolean(
props.enter ||
props.enterFrom ||
props.enterTo ||
props.leave ||
props.leaveFrom ||
props.leaveTo
) ||
// If the `as` prop is not a Fragment
(props.as ?? DEFAULT_TRANSITION_CHILD_TAG) !== Fragment ||
// If we have a single child, then we can forward the ref directly
React.Children.count(props.children) === 1
)
}
interface TransitionContextValues {
show: boolean
appear: boolean
@@ -264,7 +302,7 @@ function useNesting(done?: () => void, parent?: NestingContextValues) {
// ---
let DEFAULT_TRANSITION_CHILD_TAG = 'div' as const
let DEFAULT_TRANSITION_CHILD_TAG = Fragment
type TransitionChildRenderPropArg = MutableRefObject<HTMLDivElement>
let TransitionChildRenderFeatures = RenderFeatures.RenderStrategy
@@ -292,7 +330,9 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
...rest
} = props as typeof props
let container = useRef<HTMLElement | null>(null)
let transitionRef = useSyncRefs(container, ref)
let requiresRef = shouldForwardRef(props)
let transitionRef = useSyncRefs(...(requiresRef ? [container, ref] : ref === null ? [] : [ref]))
let strategy = rest.unmount ?? true ? RenderStrategy.Unmount : RenderStrategy.Hidden
let { show, appear, initial } = useTransitionContext()
@@ -342,10 +382,12 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
let ready = useServerHandoffComplete()
useIsoMorphicEffect(() => {
if (!requiresRef) return
if (ready && state === TreeStates.Visible && container.current === null) {
throw new Error('Did you forget to passthrough the `ref` to the actual DOM node?')
}
}, [container, state, ready])
}, [container, state, ready, requiresRef])
// Skipping initial transition
let skip = initial && !appear
@@ -495,7 +537,11 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
// @ts-expect-error
let { show, appear = false, unmount = true, ...theirProps } = props as typeof props
let internalTransitionRef = useRef<HTMLElement | null>(null)
let transitionRef = useSyncRefs(internalTransitionRef, ref)
let requiresRef = shouldForwardRef(props)
let transitionRef = useSyncRefs(
...(requiresRef ? [internalTransitionRef, ref] : ref === null ? [] : [ref])
)
// The TransitionChild will also call this hook, and we have to make sure that we are ready.
useServerHandoffComplete()
@@ -1,13 +1,12 @@
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
import { useState } from 'react'
function MyDialog({ open, close }) {
return (
<>
<Transition show={open} as={Fragment}>
<Transition show={open}>
<Dialog onClose={close} className="relative z-50">
<Transition.Child
as={Fragment}
enter="transition duration-500 ease-out"
enterFrom="opacity-0"
enterTo="opacity-100"
+2 -3
View File
@@ -1,5 +1,5 @@
import { Dialog, Menu, Portal, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
import { useState } from 'react'
import Flatpickr from 'react-flatpickr'
import { Button } from '../../components/button'
import { classNames } from '../../utils/class-names'
@@ -64,7 +64,6 @@ export default function Home() {
<Transition
data-debug="Dialog"
show={isOpen}
as={Fragment}
beforeEnter={() => console.log('[Transition] Before enter')}
afterEnter={() => console.log('[Transition] After enter')}
beforeLeave={() => console.log('[Transition] Before leave')}
@@ -79,7 +78,6 @@ export default function Home() {
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-75"
@@ -96,6 +94,7 @@ export default function Home() {
</Transition.Child>
<Transition.Child
as="div"
enter="ease-out transform duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
@@ -1,6 +1,6 @@
import { Dialog, Transition } from '@headlessui/react'
import { ExclamationIcon } from '@heroicons/react/outline'
import { Fragment, useRef, useState } from 'react'
import { useRef, useState } from 'react'
export default function Example() {
const [open, setOpen] = useState(false)
@@ -18,10 +18,9 @@ export default function Example() {
Open Dialog
</button>
</div>
<Transition.Root show={open} as={Fragment}>
<Transition.Root show={open}>
<Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={setOpen}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -35,7 +34,6 @@ export default function Example() {
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
@@ -1,5 +1,5 @@
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
import { useState } from 'react'
export default function Home() {
let [isOpen, setIsOpen] = useState(false)
@@ -37,7 +37,6 @@ export default function Home() {
<Transition
data-debug="Dialog"
show={isOpen}
as={Fragment}
beforeEnter={() => console.log('[Transition] Before enter')}
afterEnter={() => console.log('[Transition] After enter')}
beforeLeave={() => console.log('[Transition] Before leave')}
@@ -52,7 +51,6 @@ export default function Home() {
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-75"
@@ -69,6 +67,7 @@ export default function Home() {
</Transition.Child>
<Transition.Child
as="div"
enter="ease-out transform duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
@@ -1,5 +1,5 @@
import { Listbox, Transition } from '@headlessui/react'
import { Fragment, useEffect, useState } from 'react'
import { useEffect, useState } from 'react'
let people = [
'Wade Cooper',
@@ -60,7 +60,6 @@ export default function Home() {
</span>
<Transition
as={Fragment}
enter="transition duration-500 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
@@ -1,5 +1,4 @@
import { Menu, Transition } from '@headlessui/react'
import { Fragment } from 'react'
import { Button } from '../../components/button'
import { classNames } from '../../utils/class-names'
@@ -30,7 +29,6 @@ export default function Home() {
</span>
<Transition
as={Fragment}
enter="transition duration-500 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
+1 -2
View File
@@ -1,5 +1,5 @@
import { Popover, Portal, Transition } from '@headlessui/react'
import React, { Fragment, forwardRef } from 'react'
import React, { forwardRef } from 'react'
import { usePopper } from '../../utils/hooks/use-popper'
let Button = forwardRef(
@@ -33,7 +33,6 @@ export default function Home() {
<Popover.Group as="nav" aria-label="Mythical University" className="flex space-x-3">
<Popover as="div" className="relative">
<Transition
as={Fragment}
enter="transition ease-out duration-300 transform"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -18,6 +18,7 @@ export default function AppearExample() {
<span className="mb-2">Initial render</span>
<div className="grid max-w-6xl grid-cols-4 gap-4">
<Transition
as="div"
show={show}
appear={true}
unmount={true}
@@ -50,6 +51,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={false}
unmount={true}
@@ -82,6 +84,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={true}
unmount={false}
@@ -114,6 +117,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={false}
unmount={false}
@@ -152,6 +156,7 @@ export default function AppearExample() {
<span className="mb-2">Not on the initial render</span>
<div className="grid max-w-6xl grid-cols-4 gap-4">
<Transition
as="div"
show={show}
appear={true}
unmount={true}
@@ -184,6 +189,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={false}
unmount={true}
@@ -216,6 +222,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={true}
unmount={false}
@@ -248,6 +255,7 @@ export default function AppearExample() {
</Transition>
<Transition
as="div"
show={show}
appear={false}
unmount={false}
@@ -44,6 +44,7 @@ function Dropdown() {
</div>
<Transition
as="div"
show={isOpen}
enter="transition ease-out duration-75"
enterFrom="transform opacity-0 scale-95"
@@ -41,6 +41,7 @@ export default function Home() {
</div>
<Transition
as="div"
show={isOpen}
className="fixed inset-0 z-10 overflow-y-auto"
beforeEnter={() => {
@@ -78,6 +79,7 @@ export default function Home() {
{/* This element is to trick the browser into centering the modal contents. */}
<span className="hidden sm:inline-block sm:h-screen sm:align-middle"></span>&#8203;
<Transition.Child
as="div"
className="inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle"
role="dialog"
aria-modal="true"
@@ -18,7 +18,7 @@ export default function Home() {
</button>
</span>
<Transition show={isOpen} unmount={false}>
<Transition as="div" show={isOpen} unmount={false}>
<Box>
<Box>
<Box>
@@ -18,7 +18,7 @@ export default function Home() {
</button>
</span>
<Transition show={isOpen} unmount={true}>
<Transition as="div" show={isOpen} unmount={true}>
<Box>
<Box>
<Box>
@@ -43,6 +43,7 @@ export default function Home() {
function Box({ children }: { children?: ReactNode }) {
return (
<Transition.Child
as="div"
unmount={true}
enter="transition translate duration-300"
enterFrom="transform -translate-x-full"
@@ -19,6 +19,7 @@ export default function Home() {
</span>
<Transition
as="div"
show={isOpen}
appear={false}
beforeEnter={() => console.log('beforeEnter')}
@@ -160,6 +160,7 @@ function FullPageTransition() {
<div className="relative h-96 overflow-hidden rounded-lg">
{pages.map((page, i) => (
<Transition
as="div"
appear={false}
key={page}
show={activePage === i}
@@ -26,9 +26,10 @@ export default function App() {
<div className="bg-cool-gray-100 flex h-screen overflow-hidden">
{/* Off-canvas menu for mobile */}
<Transition show={mobileOpen} unmount={false} className="fixed inset-0 z-40 flex">
<Transition as="div" show={mobileOpen} unmount={false} className="fixed inset-0 z-40 flex">
{/* Off-canvas menu overlay, show/hide based on off-canvas menu state. */}
<Transition.Child
as="div"
unmount={false}
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
@@ -49,6 +50,7 @@ export default function App() {
{/* Off-canvas menu, show/hide based on off-canvas menu state. */}
<Transition.Child
as="div"
unmount={false}
enter="transition ease-in-out duration-300 transform"
enterFrom="-translate-x-full"
@@ -7,6 +7,7 @@ const TailwindToaster = () => {
{(t) => (
<Transition
appear
as="div"
show={t.visible}
className="flex transform rounded bg-white p-4 shadow-lg"
enter="transition-all duration-500"