Lali Osorio
Lali Osorio

Reputation: 11

Material-UI: How to make a Controlled accordion behaves like a Basic accordion

I'm a Junior developer, this is my first question here

I'm using a Controlled accordion because I need the icon to change dynamically but I want it to stay open when I expand a panel, even if I have expanded another panel like the basic does.

I understand that it has to do with the handleChange function but its logic is beyond me for now

I'm working with React

Any ideas ?

 const [expanded, setExpanded] = React.useState(false);

 const handleChange = (panel) => (event, isExpanded) => {
   setExpanded(isExpanded ? panel : false);
 };

 {grant?.generalRequirementsDetails.length ? (
                   <div className="grant-info-items">
                     <MuiAccordion
                       expanded={expanded === 'panel1'}
                       onChange={handleChange('panel1')}
                     >
                       <AccordionSummary
                         expandIcon={
                           expanded ? (
                             <img src={lessSVG} alt="less" />
                           ) : (
                             <img src={moreSVG} alt="more" />
                           )
                         }
                         aria-controls="panel1bh-content"
                         id="panel1bh-header"
                       >
                         <h3 sx={{ width: '33%', flexShrink: 0 }}>
                           Requisitos generales
                         </h3>
                       </AccordionSummary>
                       <AccordionDetails>
                         {grant?.generalRequirementsDetails.map(
                           (item, idx) => (
                             <div key={idx} className="grant-item">
                               <img src={tickSVG} />
                               <p>{item}</p>
                             </div>
                           ),
                         )}
                       </AccordionDetails>
                     </MuiAccordion>
                   </div>
                 ) : null}

I have another couple of panels similar to this one so when I click on one and then another I want both to be open unless I click on one of them again and that panel collapses like in the basic accordion

here is the link of the MUI component i'm talking about: https://mui.com/components/accordion/

Thank you for your help !

Upvotes: 1

Views: 2253

Answers (1)

John Li
John Li

Reputation: 7447

It looks like the accordion panels are controlled by a single state for opening and closing, therefore if one is opening the others are closed.

You could make each accordion a separate component so each will have its own expanded state. Alternatively, use an object state to store a expanded status for each accordion panel separately.

Minimal demo for both examples: stackblitz

Example for making each accordion a separate component:

export const MyPanel = ({ grant }) => {
  const [expanded, setExpanded] = React.useState(false);
  const handleChange = () => setExpanded((prev) => !prev);
  return (
    <Accordion expanded={expanded} onChange={handleChange}>
      <AccordionSummary
        expandIcon={expanded ? <RemoveIcon /> : <AddIcon />}
        aria-controls="panel1bh-content"
        id="panel1bh-header"
      >
        <h3 sx={{ width: '33%', flexShrink: 0 }}>Requisitos generales</h3>
      </AccordionSummary>
      <AccordionDetails>
        {grant?.generalRequirementsDetails.map((item, idx) => (
          <div key={idx} className="grant-item">
            <p>{`generalRequirementsDetails: ${item}`}</p>
          </div>
        ))}
      </AccordionDetails>
    </Accordion>
  );
};

export default function App() {
  return (
    <div>
      {grant?.generalRequirementsDetails.length ? (
        <div className="grant-info-items">
          {[0, 1, 2].map((item) => {
            return <MyPanel key={item} grant={grant} />;
          })}
        </div>
      ) : null}
    </div>
  );
}

Example for using an object state for controls:

export default function App() {
  const [expanded, setExpanded] = React.useState({});

  const handleChange = (panel) => () => {
    setExpanded((prev) => {
      return { ...prev, [panel]: !!!prev[panel] };
    });
  };

  return (
    <div>
      {grant?.generalRequirementsDetails.length ? (
        <div className="grant-info-items">
          <Accordion
            expanded={!!expanded['panel1']}
            onChange={handleChange('panel1')}
          >
            <AccordionSummary
              expandIcon={!!expanded['panel1'] ? <RemoveIcon /> : <AddIcon />}
              aria-controls="panel1bh-content"
              id="panel1bh-header"
            >
              <h3 sx={{ width: '33%', flexShrink: 0 }}>Requisitos generales</h3>
            </AccordionSummary>
            <AccordionDetails>
              {grant?.generalRequirementsDetails.map((item, idx) => (
                <div key={idx} className="grant-item">
                  <p>{`generalRequirementsDetails: ${item}`}</p>
                </div>
              ))}
            </AccordionDetails>
          </Accordion>
          <Accordion
            expanded={!!expanded['panel2']}
            onChange={handleChange('panel2')}
          >
            <AccordionSummary
              expandIcon={!!expanded['panel2'] ? <RemoveIcon /> : <AddIcon />}
              aria-controls="panel1bh-content"
              id="panel1bh-header"
            >
              <h3 sx={{ width: '33%', flexShrink: 0 }}>Requisitos generales</h3>
            </AccordionSummary>
            <AccordionDetails>
              {grant?.generalRequirementsDetails.map((item, idx) => (
                <div key={idx} className="grant-item">
                  <p>{`generalRequirementsDetails: ${item}`}</p>
                </div>
              ))}
            </AccordionDetails>
          </Accordion>
          <Accordion
            expanded={!!expanded['panel3']}
            onChange={handleChange('panel3')}
          >
            <AccordionSummary
              expandIcon={!!expanded['panel3'] ? <RemoveIcon /> : <AddIcon />}
              aria-controls="panel1bh-content"
              id="panel1bh-header"
            >
              <h3 sx={{ width: '33%', flexShrink: 0 }}>Requisitos generales</h3>
            </AccordionSummary>
            <AccordionDetails>
              {grant?.generalRequirementsDetails.map((item, idx) => (
                <div key={idx} className="grant-item">
                  <p>{`generalRequirementsDetails: ${item}`}</p>
                </div>
              ))}
            </AccordionDetails>
          </Accordion>
        </div>
      ) : null}
    </div>
  );
}

Upvotes: 1

Related Questions