Link one Card to another Card inside a Modal React Bootstrap

Viewed 675

I am trying to implement this:

A Card that is within an Accordion which is inside of a Modal, this Card has a Button that when it is clicked should lead to the next Card.

I could have up to 10 of these so how would I make it work in that case? More importantly I am trying to add some sort of linking ability like how you link pages with react-router-dom but within a Modal from react-bootstrap.

Link to CodeSandbox

This is the Accordion which is in a Modal with the Age button in a Card.

enter image description here

When you click on Age from the previous image it should open something like the next image which also has to be in the same Modal:

enter image description here

Here is the sample of the Accordion which is inside a Modal:

const AccordionModal = () => {
  return (
    <Accordion>
      <Card>
        <Card.Header>
          <Accordion.Toggle as={Button} variant="light" eventKey="0">
            Basic Preprocessing Variables
          </Accordion.Toggle>
        </Card.Header>
        <Accordion.Collapse eventKey="0">
          <Card.Body>
            <Button variant="primary">Age</Button>
          </Card.Body>
        </Accordion.Collapse>
      </Card>
      <Card>
   </Accordion>
  )
}
3 Answers

Returning a component in an event will not render that component.

There are two approaches here, which you can find in this sandbox.

At the component-level, you could toggle a boolean that would conditionally display the <Sample /> component though.

See conditional rendering and useState.

import { useState } from "react";
import { Button, Card } from "react-bootstrap";

const ExpandableCard = ({ label, children, ...rest }) => {
  const [expanded, setExpanded] = useState(false);

  return (
    <Card.Body {...rest}>
      <Button variant="primary" onClick={() => setExpanded(true)}>
        {label}
      </Button>
      {expanded && children}
    </Card.Body>
  );
};

If you don't want to do this at the component-level, you can keep an array of visible indices in state.

import { useState } from "react";
import { Button, Card } from "react-bootstrap";

const cards = [
  {
    label: 'Card1',
    content: () => <p>Card1 (Content)</p>
  },
  {
    label: 'Card2',
    content: () => <p>Card2 (Content)</p>
  }
]

const ExpandableCardList = () => {
  const [expandedSections, setExpandedSections] = useState([]);

  const handleClick = (index) => {
    if (expandedSections.includes(index)) return;
    setExpandedSections((prevSections) => [...prevSections, index]);
  };
  
  return cards.map(({ label, content: ExpandedContent }, index) => (
    <Card.Body key={index}>
      <Button variant="primary" onClick={() => handleClick(index)}>
        {label}
      </Button>
      {expandedSections.includes(index) && <ExpandedContent />}
    </Card.Body>
  ));
};

My idea is to keep all the state in the ModalFormTest (parent) component and render children conditionally with a switch statement, while leveraging the Pagination component from react-bootstrap for navigation. In general it could look like this:

const ModalFormTest = ({ show, handleClose }) => {
  const [page, setPage] = React.useState(1);
  const [age, setAge] = React.useState(null);

  const renderComponent = () => {
    switch (page) {
      case 1:
        return <AccordionModal changePage={(value) => setPage(value)} />;

      case 2:
        return (
          <input
            value={age}
            type="number"
            onChange={(e) => setAge(e.target.value)}
          />
        );
    }
  };
  return (
    <>
      <Modal
        size="lg"
        show={show}
        onHide={handleClose}
        aria-labelledby="example-modal-sizes-title-lg"
      >
        <Modal.Header closeButton>
          <Modal.Title id="example-modal-sizes-title-lg">
            Custom Preprocessing
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          {renderComponent()}
          <Pagination>
            <Pagination.First onClick={() => setPage(1)} />
            <Pagination.Prev
              onClick={() =>
                setPage((prev) => {
                  if (prev != 1) {
                    return prev - 1;
                  }
                })
              }
            />
            <Pagination.Item onClick={() => setPage(1)}>{1}</Pagination.Item>
            <Pagination.Item onClick={() => setPage(2)}>{2}</Pagination.Item>
            <Pagination.Next
              onClick={() =>
                setPage((prev) => {
                  if (prev != 2) {
                    return prev + 1;
                  }
                })
              }
            />
            <Pagination.Last onClick={() => setPage(2)} />
          </Pagination>
        </Modal.Body>
      </Modal>
    </>
  );
};

export default ModalFormTest;

So the user does never loose the state when he navigates around. You can incrementally add components to the switch statement and add a map to the Navigation component to avoid too much code duplication.

I forked your sandbox and modified it with my approach here.

Inside your form you can create some kind of holder for all pages and state which would take care about displaing correct page. I put most important parts of code in the answer , but this sandbox should be also helphul.

// inside <ModalFormTest/>
  const [page, setPage] = useState(0);

  const handleNextPage = () => setPage(page + 1);

  const sections = [
    <AccordionModal key={0} onNextPage={handleNextPage} />,
    <NextSection key={1} onNextPage={handleNextPage}>
      I am the 2 page
    </NextSection>,
    /* and others */
    <NextSection key={9}>I am the last one</NextSection>
  ];

  <Modal.Body>{sections[page]}</Modal.Body>

// inside AccordionModal
const AccordionModal = (onNexPage) => { 

...

<Button variant="primary" onClick={onNextPage}>Age</Button>
// inside NextSection component
const NextSection = ({ onNextPage, children }) => {
  return (
    <div>
      <p>{children}</p>
      <Button onClick={onNextPage}>Next</Button>
    </div>
  );
};


  [1]: https://codesandbox.io/s/magical-worker-3qmo5?fontsize=14&hidenavigation=1&theme=dark
Related