Search code examples
javascriptcssreactjsstyled-components

Accordion expand card with react and css


I'm trying to make this card in such a way that when you click on view details, the card's blue footer should expand in height and shows the lorem text, and then clicking on another card the previous card will get collapsed. The issue is when I click on view details the card's blue bottom is not expanding with the lorem. Here is my sandbox: https://codesandbox.io/s/wonderful-bohr-by01z?file=/src/App.js

App.js:

import { Card, Footer, Header } from "./styles";
import { useState } from "react";

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

  return (
    <>
      <Card>
        <Header>last viewed: {null}</Header>
        <Footer>
          <span onClick={() => setExpanded(!expanded)}>View Details</span>
          {expanded && (
            <div className="accodion">
              Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eos,
              facilis. Lorem, ipsum dolor sit amet consectetur adipisicing elit.
              Eos, facilis. Lorem, ipsum dolor sit amet consectetur adipisicing
              elit. Eos, facilis. Lorem, ipsum dolor sit amet consectetur
              adipisicing elit. Eos, facilis.
            </div>
          )}
        </Footer>
      </Card>
    </>
  );
}

styles.js:

import styled from "styled-components";

const Card = styled.div`
  background-color: ${({ isEmpty }) => (isEmpty ? "#FAFAFA" : "white")};
  height: 100%;
  border-radius: 20px;
  box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  margin: 8px;
`;

const DropDown = styled.div`
  background-color: lightblue;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
`;

const Header = styled.div`
  display: flex;
  justify-content: space-between;
  margin-top: -40;
  font-size: 10px;
  color: #7894b0;
  margin: 16px;
`;
const Footer = styled.div`
  background-color: rgb(242, 247, 251);
  width: 100%;
  height: 50px;
  font-size: 12px;
  line-height: 12px;
  color: #4f4f4f;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
  cursor: pointer;

  .accodion {
    padding: 30px;
  }
`;

export { Card, Header, Footer, DropDown };

Solution

  • For solution need additional switch class .show and get the height for the current footer with useRef. Also adding second useState where will keep the whole footer height. sandbox example

    Styled-components

    const Card = styled.div`
      background-color: ${({ isEmpty }) => (isEmpty ? '#FAFAFA' : 'white')};
      height: 100%;
      border-radius: 20px;
      box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
      overflow: hidden;
      margin: 8px;
    `;
    
    const DropDown = styled.div`
      background-color: lightblue;
      display: flex;
      justify-content: center;
      flex-direction: column;
      align-items: center;
    `;
    
    const Header = styled.div`
      display: flex;
      justify-content: space-between;
      margin-top: -40;
      font-size: 10px;
      color: #7894b0;
      margin: 16px;
    `;
    const Footer = styled.div`
      background-color: rgb(242, 247, 251);
      width: 100%;
      height: 35px; /* changed */
      font-size: 12px;
      line-height: 12px;
      color: #4f4f4f;
      display: flex;
      /* justify-content: center; */
      flex-direction: column;
      align-items: center;
      cursor: pointer;
      transition: all 0.3s ease-in-out; /* added */
      overflow: hidden; /* added */
    
      span {
        padding: 12px 0;
      }
    
      .accodion {
        padding: 10px 15px 15px; /* changed */
      }
    
      &.show {
        height: ${({ setHeight }) => setHeight}px;
      }
    `;
    

    App.js

    import { Card, Footer, Header } from "./styles";
    import { useState, useEffect, useRef } from "react";
    
    export default function App() {
      const [expanded, setExpanded] = useState(false);
      const [accodionHeight, setAccodionHeight] = useState(0);
      const ref = useRef(null);
    
      const open = () => setExpanded(!expanded);
    
      useEffect(() => {
        const getHeight = ref.current.scrollHeight;
        setAccodionHeight(getHeight);
      }, [expanded]);
    
      return (
        <>
          <Card>
            <Header>last viewed: {null}</Header>
            <Footer
              onClick={open}
              className={expanded ? "show" : ""}
              setHeight={accodionHeight}
              ref={ref}
            >
              <span>View Details</span>
              <div className="accodion" ref={ref}>
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eos,
                facilis. Lorem, ipsum dolor sit amet consectetur adipisicing elit.
                Eos, facilis. Lorem, ipsum dolor sit amet consectetur adipisicing
                elit. Eos, facilis. Lorem, ipsum dolor sit amet consectetur
                adipisicing elit. Eos, facilis.
              </div>
            </Footer>
          </Card>
        </>
      );
    }