Group Disclosures (Accordian) from Headless UI

Viewed 970

I've just started using Headless UI. I'm trying to use the Disclosure component from Headless UI to render my job experiences. Basically, I need "n" number of Disclosures which will be dynamically rendered and whenever one Disclosure is opened the others should close.

I am able to render the Disclosures dynamically, and they all have their individual states. (opening/closing a disclosure doesn't affect the other Disclosure). All I want to do is to have only one disclosure open at a time. Opening another Disclosure should close all the remaining Disclosures. I have gone through their docs but couldn't find a way to manage multiple Disclosure states together.

Here is my code:

    import React, { useContext } from "react";
    import { GlobalContext } from "../data/GlobalContext";
    import { Tab, Disclosure } from "@headlessui/react";
    import ReactMarkdown from "react-markdown";

    const Experience = () => {
      const { data } = useContext(GlobalContext);
      const expData = data.pageContent.find(
        (content) => content.__component === "page-content.experience-page-content"
      );

      return (
        <div className="container h-screen">
          <div className="flex h-full flex-col items-center justify-center">
            <h3 className="">{expData.pageTitle}</h3>
            <div className="flex min-h-[600px] flex-col">

              {expData.jobs.map((job, i) => (
                <Disclosure key={job.companyName} defaultOpen={i === 0}>
                  <Disclosure.Button
                    key={job.companyName + "_tab"}
                    className="px-4 py-3 dark:text-dark-primary"
                  >
                    {job.companyName}
                  </Disclosure.Button>
                  <Disclosure.Panel key={job.companyName + "_panel"}>
                    <p className="">
                      <span className="">{job.designation}</span>
                      <span className="">{" @ "}</span>
                      <span className="">{job.companyName}</span>
                    </p>
                    <p className="">{job.range}</p>
                    <ReactMarkdown className="">
                      {job.workDescription}
                    </ReactMarkdown>
                  </Disclosure.Panel>
                </Disclosure>
              ))}

            </div>
          </div>
        </div>
      );
    };

    export default Experience;

It would be really helpful if someone could help me with this. Thanks.

1 Answers
<template>
  <div class="mx-auto w-full max-w-md space-y-3 rounded-2xl bg-white p-2">
    <Disclosure v-for="(i, idx) in 3" :key="i" v-slot="{ open, close }">
      <DisclosureButton
        :ref="el => (disclosure[idx] = close)"
        class="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"
        @click="hideOther(idx)"
      >
        <span> What is your refund policy? {{ open }} </span>
      </DisclosureButton>

      <DisclosurePanel class="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.
      </DisclosurePanel>
    </Disclosure>
  </div>
</template>

<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'

const disclosure = ref([])

const hideOther = id => {
  disclosure.value.filter((d, i) => i !== id).forEach(c => c())
}
</script>

here how I did it in Vue.

Related