Search code examples
reactjsexpandframer-motioncard

How to make only one card out of a grid of cards expand at a time?


I have a grid of 'cards' displaying games that when clicked expand down to show more information however when I click on one ALL of the cards expand open and I'm wanting only one to expand at a time.

Here is a codesandbox of what I have so far. Thanks for any help!


Solution

  • Here's working component. The component state must reflect currently open card or null if no card is open. Also changed class to className since it's the proper way to set component class from CSS

    export const GameCard = () => {
    
      const [openedCard, setOpenedCard] = useState(null);
    
      return (
        <div id="card_wrapper">
          {games.map((game) => (
            <motion.div
              transition={{ layout: { duration: 1, type: "spring" } }}
              layout
              onClick={() => setOpenedCard(game.id)}
              key={game.id}
              className="card"
              name={game.name}
              style={{
                backgroundImage: `url(${game.background})`,
                backgroundSize: "cover",
                backgroundRepeat: "no-repeat"
              }}
            >
              <motion.h2 layout="position">
                {" "}
                <img src={game.logo} alt={game.alt} />
              </motion.h2>
              {openedCard === game.id && (
                <motion.div
                  initial={{ opacity: 0 }}
                  animate={{ opacity: 1 }}
                  transition={{ duration: 1 }}
                  className="expand"
                >
                  <p> {game.description} </p>
                </motion.div>
              )}
            </motion.div>
          ))}
        </div>
      );
    };
    

    The issue of expanding all cards when any is clicked had nothing to do with React but it's caused by #card_wrapper flex-box settings: align-items is by default set to stretch but you need it to be flex-start instead.

    #card_wrapper {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      align-items: flex-start;
    }
    

    BTW. You had #card_wrapper defined twice in your stylesheet - made one instead.

    And the last but least - remember to use className prop instead of class - you need to fix games.js for that issue.