Source: layout/AccordionBody.js

import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, tagPropType } from 'reactstrap/src/utils';
import { AccordionContext } from 'reactstrap/src/AccordionContext';
import { Collapse } from 'reactstrap';
import { emptyObject, processIncludeVariables, useReactorNavigation } from '../..';
import { useSelector } from 'react-redux';

/* agregar include_variables y use_spinner */
/**
 * Componente que renderiza el body de un Accordion de Bootstrap.
 * @name Reactor.Components.Layout.AccordionBody
 * @param {string} id Identificador unico de body 
 * @param {string} options Json string de propiedades que van al Collapse dentro del Body
 * @param {string} href Operacion que se va a ejecutar con tarjet operation al abrir el panel
 * @param {string} data Json string con data que se envia a la operacion (solo si href tiene un valor)
 * @param {string?} include_variables Lista de variables que se incluyen en la data (separadas por espacio)
 * @param {string?} context Contexto contenedor de las variables que se van a incluir, es requerido si se incluyen variables
 * @param {BooleanString} [use_spinner = "true"] Si es true se muestra un spinner mientras se espera la respuesta del servidor
 * @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" href='Operacion' data='{"propiedad": "valor"}'>
      <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>
*/
export const AccordionBody = ({children, id, options, href, data, use_spinner = "true", include_variables, context, debug = "false"}) => {
  return (
    <AccordionBodyBs accordionId={id} href={href} data={data} include_variables={include_variables} context={context} use_spinner={use_spinner} {...(options ? JSON.parse(options) : {})}>
      {children}
    </AccordionBodyBs>
  );
}

const propTypes = {
  tag: tagPropType,
  className: PropTypes.string,
  cssModule: PropTypes.object,
  innerRef: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.string,
    PropTypes.func,
  ]),
  children: PropTypes.node,
  accordionId: PropTypes.string.isRequired,
};

const defaultProps = {
  tag: 'div'
};

const AccordionBodyBs = (props) => {
  const {
    href,
    data,
    use_spinner,
    include_variables,
    context,
    className,
    cssModule,
    tag: Tag,
    innerRef,
    children,
    accordionId,
    ...attributes
  } = props;

  const variablesState = useSelector(state => context === "_" ? state.app.variables : state.app.variables[context] ) || null;

  const getVariables = useCallback(() => {
    return context && processIncludeVariables({variablesState, include_variables}); 
  }, [context, variablesState, include_variables]);

  const navigation = useReactorNavigation({data, navOptions: emptyObject, target: "operation", to: href});
  
  const { open } = useContext(AccordionContext);

  const classes = mapToCssModules(classNames(
    className,
    'accordion-collapse',
  ), cssModule);

  const isOpen = useMemo(()=>Array.isArray(open) ? open.includes(accordionId) : open === accordionId, [accordionId, open]);

  const [ loaded, setLoaded ] = useState(false);

  useEffect(()=>{
    if(loaded){
      if(href && isOpen){
        navigation.execute({event: "open", ...getVariables()});
      }
      if(href && !isOpen){
        navigation.execute({event: "close", ...getVariables()});
      }
    }else{
      const timer = setTimeout(()=>{
        setLoaded(true);
      }, 50);
      return ()=>clearTimeout(timer);
    }
  }, [isOpen]);

  return (
    <Collapse
      {...attributes}
      className={classes}
      ref={innerRef} 
      isOpen={isOpen}>
      <Tag className="accordion-body">{children}</Tag>
    </Collapse>    
  );
};

AccordionBodyBs.propTypes = propTypes;
AccordionBodyBs.defaultProps = defaultProps;