HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptTip

Collapsible content React components

Submitted by: @import:30-seconds-of-code··
0
Viewed 0 times
collapsiblecomponentsreactcontent

Problem

React makes collapsible content components a breeze with its state management and component composition. Simply put, you get full control over the content and its visibility, allowing you to create collapses, accordions, tabs and carousels.
For a simple collapsible content component, you need the useState() hook to manage the state of the collapsible content. Then, using a <button> to toggle the visibility of the content, you can make the content collapse or expand by changing the state.
For accessibility purposes, it's a good idea to use the aria-expanded attribute to indicate the state of the collapsible content.
<code-tabs full-width="true">
</code-tabs>

Solution

const Collapse = ({ collapsed, children }) => {
  const [isCollapsed, setIsCollapsed] = React.useState(collapsed);

  return (
    <>
      <button
        className="collapse-button"
        onClick={() => setIsCollapsed(!isCollapsed)}
      >
        {isCollapsed ? 'Show' : 'Hide'} content
      </button>
      <div
        className={`collapse-content ${isCollapsed ? 'collapsed' : 'expanded'}`}
        aria-expanded={isCollapsed}
      >
        {children}
      </div>
    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Collapse>
    <h1>This is a collapse</h1>
    <p>Hello world!</p>
  </Collapse>
);


For accessibility purposes, it's a good idea to use the aria-expanded attribute to indicate the state of the collapsible content.
<code-tabs full-width="true">
</code-tabs>
Accordions are very similar, but we need to manage the state a little differently, holding the index of the active accordion item. We can then toggle the visibility of the content based on the active index.
In order to make the component easier to read, we'll extract the AccordionItem component to handle each accordion item. The Accordion component will manage the state and render the items.
As an additional feature, we can pass a callback function to the Accordion component to handle the click event on each accordion item. We may also want to filter out any non-AccordionItem children.

Code Snippets

const Collapse = ({ collapsed, children }) => {
  const [isCollapsed, setIsCollapsed] = React.useState(collapsed);

  return (
    <>
      <button
        className="collapse-button"
        onClick={() => setIsCollapsed(!isCollapsed)}
      >
        {isCollapsed ? 'Show' : 'Hide'} content
      </button>
      <div
        className={`collapse-content ${isCollapsed ? 'collapsed' : 'expanded'}`}
        aria-expanded={isCollapsed}
      >
        {children}
      </div>
    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Collapse>
    <h1>This is a collapse</h1>
    <p>Hello world!</p>
  </Collapse>
);
.collapse-button {
  display: block;
  width: 100%;
}

.collapse-content.collapsed {
  display: none;
}

.collapsed-content.expanded {
  display: block;
}
const AccordionItem = ({ label, isCollapsed, handleClick, children }) => {
  return (
    <>
      <button className="accordion-button" onClick={handleClick}>
        {label}
      </button>
      <div
        className={`accordion-item ${isCollapsed ? 'collapsed' : 'expanded'}`}
        aria-expanded={isCollapsed}
      >
        {children}
      </div>
    </>
  );
};

const Accordion = ({ defaultIndex, onItemClick, children }) => {
  const [bindIndex, setBindIndex] = React.useState(defaultIndex);

  const changeItem = itemIndex => {
    if (typeof onItemClick === 'function') onItemClick(itemIndex);
    if (itemIndex !== bindIndex) setBindIndex(itemIndex);
  };
  const items = children.filter(item => item.type.name === 'AccordionItem');

  return (
    <>
      {items.map(({ props }) => (
        <AccordionItem
          isCollapsed={bindIndex !== props.index}
          label={props.label}
          handleClick={() => changeItem(props.index)}
          children={props.children}
        />
      ))}
    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Accordion defaultIndex="1" onItemClick={console.log}>
    <AccordionItem label="A" index="1">
      Lorem ipsum
    </AccordionItem>
    <AccordionItem label="B" index="2">
      Dolor sit amet
    </AccordionItem>
  </Accordion>
);

Context

From 30-seconds-of-code: collapsable-content-components

Revisions (0)

No revisions yet.