Source: layout/Accordion.js

import React, { useMemo, useState } from 'react';
import { getStyleObjectFromString } from "../../utils";
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, tagPropType } from 'reactstrap/src/utils';
import { AccordionContext } from 'reactstrap/src/AccordionContext';

/**
 * Componente que renderiza un Accordion de Bootstrap.
 * @name Reactor.Components.Layout.Accordion
 * @param {string} default_open panel que estarĂ¡ abierto por defecto
 * @class
 * @example
<accordion default_open="1">
  <accordion_item options='{"className":"card card-style"}'>
    <div class="card-header d-flex">
      <div class="align-self-center">
        <h6 class="pt-1 font-14 font-600">Factura 0000-111111</h6>
      </div>
      <div class="align-self-center ms-auto text-end">
        <h6 class="pt-1 font-14 font-600">10/03/2022</h6>
      </div>
    </div>
    <accordion_header target="1">
      <!-- header --> 
      <div class="card-body d-flex mb-2" style="height: auto; padding: 0.5rem 1rem;">
        <div class="align-self-center" style="max-width: 240px">
          <h6 class="mb-n1 font-12 opacity-60">Heladera con Freezer Gafa 282 Litros</h6>
          <h6 class="pt-1 mb-n1 font-12 opacity-60">Smart TV 43" Samsung</h6>
          <h6 class="pt-1 mb-n1 font-12 opacity-60">Aire Acondicionado Split</h6>
        </div>
        <div class="align-self-center ms-auto text-end pt-1">
          <p class="mb-0 font-11 opacity-70" style="line-height: 10px">Monto a pagar</p>
          <p class="mb-0 font-15 color-blue-dark font-700">$20.000,00</p>
          <p class="mb-0 font-11 opacity-70" style="line-height: 10px">Vence</p>
          <p class="mb-0 font-13 color-blue-dark font-700">19/03/2022</p>
        </div>
      </div>
      <!-- comprobante fin -->  
    </accordion_header>
    <accordion_body id="1">
      <strong>
        This is the second item's accordion body.
      </strong>
      You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the{' '}
      <code>
        .accordion-body
      </code>
      , though the transition does limit overflow.   
    </accordion_body>
  </accordion_item>
  <accordion_item>
    <accordion_header target="2">
      Titulo 2
    </accordion_header>
    <accordion_body id="2">
      <strong>
        This is the second item's accordion body.
      </strong>
      You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the{' '}
      <code>
        .accordion-body
      </code>
      , though the transition does limit overflow.   
    </accordion_body>
</accordion>
<style>
  .accordion-item:first-of-type .accordion-button {
    border-top-left-radius: 0px !important;
    border-top-right-radius: 0px !important;
  }
</style>

 */

export const Accordion = ({children, default_open, options}) => {
  return <UncontrolledAccordionBs defaultOpen={default_open} {...(options ? JSON.parse(options) : {})}>{children}</UncontrolledAccordionBs>;
}


const propTypesAccordionBs = {
  tag: tagPropType,
  className: PropTypes.string,
  cssModule: PropTypes.object,
  innerRef: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.string,
    PropTypes.func,
  ]),
  children: PropTypes.node,
  flush: PropTypes.bool,
  open: PropTypes.oneOfType([PropTypes.array, PropTypes.string]).isRequired,
  toggle: PropTypes.func.isRequired,
};

const defaultPropsAccordionBs = {
  tag: 'div'
};

const AccordionBs = (props) => {
  const {
    flush,
    open,
    toggle,
    className,
    cssModule,
    tag: Tag,
    innerRef,
    ...attributes
  } = props;
  const classes = mapToCssModules(classNames(
    className,
    'accordion',
    {
      'accordion-flush': flush
    }
  ), cssModule);

  const accordionContext = useMemo(() => ({
    open,
    toggle,
  }));

  return (
    <AccordionContext.Provider value={accordionContext}>
      <Tag {...attributes} className={classes} ref={innerRef} />
    </AccordionContext.Provider>
  );
};

AccordionBs.propTypes = propTypesAccordionBs;
AccordionBs.defaultProps = defaultPropsAccordionBs;



const propTypesUncontrolledAccordionBs = {
  tag: tagPropType,
  className: PropTypes.string,
  cssModule: PropTypes.object,
  innerRef: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.string,
    PropTypes.func,
  ]),
  children: PropTypes.node,
  defaultOpen: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
  stayOpen: PropTypes.bool,
};

const defaultPropsUncontrolledAccordionBs = {
  tag: 'div'
};

const UncontrolledAccordionBs = ({ defaultOpen, stayOpen, ...props }) => {
  const [open, setOpen] = useState(defaultOpen || (stayOpen ? [] : undefined));
  const toggle = (id) => {
    if (stayOpen) {
      open.includes(id) ? setOpen(open.filter(accordionId => accordionId !== id)) : setOpen([...open, id]);
    } else {
      open === id ? setOpen(undefined) : setOpen(id);
    }
  };

  return <AccordionBs {...props} open={open} toggle={toggle} />;
};

UncontrolledAccordionBs.propTypes = propTypesUncontrolledAccordionBs;
UncontrolledAccordionBs.defaultProps = defaultPropsUncontrolledAccordionBs;