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:
@@ -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"
|
||||
|
||||
@@ -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,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>​
|
||||
<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"
|
||||
|
||||
Reference in New Issue
Block a user