Accordion
Accordion that maps through an array (accordionList) of objects (ENTRIES: title, content) and returns an expandable accordion item.
import React, { useState } from 'react';
import rightArrow from '../../images/right-arrow.svg';
import styled from 'styled-components';
const StyledAccordion = styled.dl`
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  dt {
    display: flex;
    justify-content: space-between;
    cursor: pointer;
  }
  dt[aria-expanded='true'] img {
    transform: rotateZ(90deg);
  }
  .list-group {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    border-top: 1px solid black;
    padding: 1.5rem 0 3.5rem 0;
  }
  img {
    transition: all 0.35s ease;
  }
  dd {
    overflow: hidden;
    max-height: 50rem;
    transition: max-height 1.5s ease-in-out;
  }
  dd[aria-expanded='true'] {
    max-height: 0px;
    transition: max-height 1s cubic-bezier(0, 1, 0, 1);
  }
`;
const Accordion = ({ accordionList }) => {
  const [open, setOpen] = useState(accordionList.map(accordion => false));
  const [focused, setFocused] = useState(accordionList.map(accordion => false));
  const toggleAccordion = idx => {
    setOpen({ ...open, [idx]: !open[idx] });
  };
  const handleKeyboardListener = (e, idx) => {
    if (e.key === ' ' && focused[idx]) {
      e.preventDefault();
      let newOpen = open;
      newOpen[idx] = !open[idx];
      setOpen(newOpen);
    }
  };
  const handleFocus = (e, idx) => {
    let newFocused = focused;
    newFocused[idx] = !focused[idx];
    setFocused(newFocused);
  };
  return (
    <StyledAccordion>
      {accordionList.map((acc, idx) => (
        <React.Fragment key={idx}>
          <div className="list-group">
            <dt
              onClick={() => toggleAccordion(idx)}
              onKeyDown={e => handleKeyboardListener(e, idx)}
              onFocus={e => handleFocus(e, idx)}
              onBlur={e => handleFocus(e, idx)}
              aria-expanded={open[idx]}
              tabIndex={'0'}
              role="button"
            >
              {acc.title}
              <img src={rightArrow} alt="collapse accordion" />
            </dt>
            <dd aria-expanded={!open[idx]}>{acc.content}</dd>
          </div>
        </React.Fragment>
      ))}
    </StyledAccordion>
  );
};
export default Accordion;