'How to collapse Siblings with Headless UI
To make the accordion component with Headless UI, I have used Disclosure component. But I have a problem to control the collapse/expand state for it's siblings.
So, I want to close other siblings when I open one, but Disclosure component is only supporting internal render props, open and close. So, I can't control it outside of the component and can't close others when I open one.
import { Disclosure } from '@headlessui/react'
import { ChevronUpIcon } from '@heroicons/react/solid'
export default function Example() {
return (
<div className="w-full px-4 pt-16">
<div className="mx-auto w-full max-w-md rounded-2xl bg-white p-2">
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button className="flex w-full justify-between rounded-lg bg-purple-100 px-4 py-2 text-left text-sm font-medium text-purple-900 hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75">
<span>What is your refund policy?</span>
<ChevronUpIcon
className={`${
open ? 'rotate-180 transform' : ''
} h-5 w-5 text-purple-500`}
/>
</Disclosure.Button>
<Disclosure.Panel className="px-4 pt-4 pb-2 text-sm text-gray-500">
If you're unhappy with your purchase for any reason, email us
within 90 days and we'll refund you in full, no questions asked.
</Disclosure.Panel>
</>
)}
</Disclosure>
<Disclosure as="div" className="mt-2">
{({ open }) => (
<>
<Disclosure.Button className="flex w-full justify-between rounded-lg bg-purple-100 px-4 py-2 text-left text-sm font-medium text-purple-900 hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75">
<span>Do you offer technical support?</span>
<ChevronUpIcon
className={`${
open ? 'rotate-180 transform' : ''
} h-5 w-5 text-purple-500`}
/>
</Disclosure.Button>
<Disclosure.Panel className="px-4 pt-4 pb-2 text-sm text-gray-500">
No.
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
</div>
)
}
How do we control the close/open state outside of the component?
Solution 1:[1]
I don't think so it's possible using HeadlessUI, although you can create your own Disclosure
like component.
- Lift the state up to the parent component by creating a
disclosures
state that stores all the information about the disclosures. - Loop over the disclosures using
map
and render them. - Render a button that toggles the
isClose
property of the disclosures and also handles thearia
attributes. - On button click, toggle the
isOpen
value of the clicked disclosure and close all the other disclosures.
Checkout the snippet below:
import React, { useState } from "react";
import { ChevronUpIcon } from "@heroicons/react/solid";
export default function Example() {
const [disclosures, setDisclosures] = useState([
{
id: "disclosure-panel-1",
isOpen: false,
buttonText: "What is your refund policy?",
panelText:
"If you're unhappy with your purchase for any reason, email us within 90 days and we'll refund you in full, no questions asked."
},
{
id: "disclosure-panel-2",
isOpen: false,
buttonText: "Do you offer technical support?",
panelText: "No."
}
]);
const handleClick = (id) => {
setDisclosures(
disclosures.map((d) =>
d.id === id ? { ...d, isOpen: !d.isOpen } : { ...d, isOpen: false }
)
);
};
return (
<div className="w-full px-4 pt-16">
<div className="mx-auto w-full max-w-md rounded-2xl bg-white p-2 space-y-2">
{disclosures.map(({ id, isOpen, buttonText, panelText }) => (
<React.Fragment key={id}>
<button
className="flex w-full justify-between rounded-lg bg-purple-100 px-4 py-2 text-left text-sm font-medium text-purple-900 hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75"
onClick={() => handleClick(id)}
aria-expanded={isOpen}
{...(isOpen && { "aria-controls": id })}
>
{buttonText}
<ChevronUpIcon
className={`${
isOpen ? "rotate-180 transform" : ""
} h-5 w-5 text-purple-500`}
/>
</button>
{isOpen && (
<div className="px-4 pt-4 pb-2 text-sm text-gray-500">
{panelText}
</div>
)}
</React.Fragment>
))}
</div>
</div>
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |