Is there a way to synchronize the expanded row data with the columns of the main table in Material UI's collapsible table?

I have a feature where, upon clicking the expand icon, a row is expanded. I am looking to align the data of these expanded rows with the columns of the main table. To achieve this, I am utilizing Material UI's collapsible table component!

How can I implement this in HTML/CSS?

To better illustrate the issue, I have created an example! You can view it on CodeSandbox:

https://codesandbox.io/s/heuristic-allen-im8tj9?file=/src/App.js

The nested row (expanded row) fields are not currently aligning with their parent columns. How can I properly align them with their parent columns?

I am aiming for something similar to this:

https://i.sstatic.net/6jRqW.jpg

Any assistance or suggestions would be greatly appreciated. Thank you!

Answer №1

To achieve a responsive table, I utilized a unique method by passing a reference to the main table's TableHead's first TableRow, which typically contains column names. This ref was then passed as a prop in the expanded row's Table component, allowing me to use both colgroup and getBoundingClientRect().width from the ref's children. The dynamic adjustment of column widths in the expanded rows table based on the main table's columns was successfully implemented with this approach. Below is a sample code snippet that demonstrates this technique:

const MainTableComp = () => {
  ...
  const ref = useRef(null);
  ...
  return <TableContainer>
    <Table>
      <TableHead>
        <TableRow ref={ref}>
          <TableCell>Order Ids</TableCell>
          ...
            <ExpandedRow mainTableEl={ref}>
            ...
};

const ExpandedRow = ({ mainTableEl }) => {
  ...
  const [colWidths, setColWidths] = useState([]);
  ...
  useEffect(() => {
    setColWidths(
      Array.from(tableEl.current?.children || []).map(c => {
        return c.getBoundingClientRect().width
      })
    )
  }, [tableEl.current?.children[0].getBoundingClientRect()]); // if one column's width changes, the rest's, and 0's, would be too

  return <Fragment>
    ...
      <Collapse in={open} timeout="auto" unmountOnExit>
        <Table>
          <colgroup>
            {
              colWidths.map((cW, idx) => {
                return <col key={idx} style={{ width: cW }}></col>
              })
            }
          </colgroup>
          ...

};


Answer №2

Encountered a similar issue that required creating an 'invisible' header for the subitems and dynamically adjusting column widths. The solution that worked for me is as follows:

useLayoutEffect(() => {
  if (openedItem) {
    const hardcodeWidths = () => {
      const headers = Array.from(document.getElementsByClassName('headerCell'));
      const subTableParentRow = document.getElementsByClassName(`expandable_${openedItem}`)[0];
      const subTableColumns = subTableParentRow.getElementsByClassName('subTableColumn');
      headers.forEach((header, i) => {
        (subTableColumns[i] as HTMLElement).style.width = `${header.clientWidth}px`;
      });
    }
    
    hardcodeWidths();
    addEventListener('resize', hardcodeWidths);
    return () => {
      removeEventListener('resize', hardcodeWidths);
    }
  }
}, [openedItem])
<Table sx={{ minWidth: 650 }} aria-label="Talaria table" >
  <TableHead className={style.tableHead}>
    <TableRow>
      {props.hasStickyFirstRow && <TableCell />}
      {props.headers.cells.map((cell, index) => (
        <TableCell className='headerCell' align="center" key={index}>{cell.displayValue}</TableCell>  
      ))}
      {props.hasStickyFirstRow && <TableCell />}
    </TableRow>

    {props.hasStickyFirstRow &&
      <TableRow className={style.stickyHeader}>
        <TableCell> <LockedProtected className={style.tableLockIcon}/> </TableCell>
        {props.headers.cells.map((cell, index) => (
          <TableCell align="center" key={index}>
            {props.selectedStickyRow[cell.key]}
          </TableCell>
        ))}
        <TableCell/>
      </TableRow>
    }
  </TableHead>
  <TableBody>
    {(rowsPerPage > 0
      ? props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
      : props.rows
    ).map((row) => {
      return(
        <>
          <TableRow
            key={row[props.uniqueElement]}
            sx={{
              '&:last-child td, &:last-child th': { border: 0 },
              ...(!(props.hasStickyFirstRow) && {
              cursor: 'pointer',
              }),
            }}
            className={style.tableBody}
            onClick={props.canClickRow ? () => props.onRowClick(row) : undefined}
          >
            { props.hasStickyFirstRow && <TableCell>
              <IconButton
                aria-label="expand row"
                size="small"
                onClick={() => setOpenedItem(openedItem === row[props.uniqueElement] ? '' : row[props.uniqueElement] )}
              >
                {openedItem === row[props.uniqueElement] ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
              </IconButton>
            </TableCell>}
            {props.headers.cells.map((cell) => (
              <TableCell align="center">
                {row[cell.key]}
              </TableCell>
            ))}
            { props.hasStickyFirstRow && <TableCell>
              <Radio
                checked={row[props.uniqueElement] === props.selectedRadioRowId}
                onChange={() => props.setSelectedRadioRowId && props.setSelectedRadioRowId(row[props.uniqueElement])}
                // TODO: disabled  // Here will need to add a logic to be disabled or maybe  primary color or 'secondary' if the table is disabled
              />
            </TableCell>}
          </TableRow>
          { props.hasStickyFirstRow && <TableRow className={`expandable_${row[props.uniqueElement]}`}>
            {/* TODO: when BE comes with a data format please make sure to change the keys (also up) */}
            {/* Added this table cell for alignment purposes */}
            <TableCell key={'dummyStartCell'} style={{padding: 0}}/> 
            <TableCell style={{ padding: 0 }} colSpan={props.headers.cells.length}>
              <Collapse in={openedItem === row[props.uniqueElement]} timeout="auto" unmountOnExit>
                <Table >
                  <colgroup>
                    {props.headers.cells.map(() => (
                      <col className='subTableColumn' />
                    ))}
                  </colgroup>
                  <TableBody>
                    {row.rowDetails.map((rowDetails:any, key:string) => (
                      <TableRow key={key} className='subtableCell' sx={{ '&:last-child td, &:last-child th': { border: 0 } }} >
                        {props.headers.cells.map((cell, index) => (
                          <TableCell align="center" key={cell.key}>
                            {rowDetails[cell.key]}
                          </TableCell>
                        ))}
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </Collapse>
            </TableCell>
            {/* Added this table cell for alignment purposes */}
            <TableCell key={'dummyEndCell'} style={{padding: 0}}/> 
        </TableRow> }
      </>
    )})}
  </TableBody>
</Table>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If the text is too wide for the parent div width, wrap it in a child div

<div class='jfmfs-friend' id='123'> <input type='checkbox'/> <img src='id.jpg'/> <div class='friend-name'>Himanshu Yadav</div> </div> I am looking to modify the st ...

Strategies for identifying CSS properties within a div element

I am attempting to identify the CSS property display of a div. If it is set to display:block, I want to change the icon to -. If it is set to display:none, I want to change the icon to +. I have tried using the following code but it does not seem to work: ...

Having trouble with the PHP code for sending an email using a basic form

This is an example from my HTML page. <form method="POST" action="sm.php"> <p>Login</p><br /> Username: <input type="text" size="10" maxlength="50" name="un"> <br /> Password: <input type="text" size="10 ...

Can the @font-face src URL be modified?

We have integrated FontAwesome with Bootstrap on our website. However, we encountered an issue when using FA with a custom minifier as it tries to load fonts from a relative path, leading to a 404 error due to the structure of the minified URL. To address ...

Ways to navigate through a webpage without encountering any overflow issues

My window is too small to scroll, but I still need the ability to do so. Is it possible to scroll even when the height of the container is not large enough to display the scrollbar? Below is the code I am using to achieve scrolling: setTimeout(function() ...

What is the best way to create a unique transition for every component?

I have three elements that I would like to display with a transition effect when they appear or disappear. There is one main element and the other two show up when you click the corresponding button. You can view my current code sample here: https://jsfid ...

Executing a custom object function in AngularJS by using the ng-click directive

As I navigate my way through AngularJS, I find myself grappling with the concept of calling a custom method of an object and wonder if there's a simpler approach: https://jsfiddle.net/f4ew9csr/3/ <div ng-app="myApp" ng-controller="myCtrl as myCtr ...

Unforeseen outcomes when setting background colors with Anime.js

I've recently started experimenting with Anime.js. I have a piece of code that I'm using to animate a div element with an id of a. anime({ targets: "#a", translateX: 250, color: "#fff", backgroundColor: "hsl(200, 50%, 50%)", ...

When the md-menu is clicked, the Top Nav shifts upwards along with the mdDialog buttons

Currently, I am in the process of developing a website using AngularJS and ASP.NET, incorporating Angular Material. However, I encountered an issue where scrolling on the page causes the top navigation to move up the body or essentially "disappear" when cl ...

How can you center a div at the top of another div?

I am working with a nested div structure and trying to center the inner content of one div within another. Is it possible to achieve this using CSS? <div id="outer"> bla, bla....... <div id="inner"> <p>Test</p> </div> ...

Updating the `App` component is not permissible during the rendering of the `UserTable` component

Currently, I am diving into React Hooks in functional components and I came across the React Hooks tutorial. However, as I followed along, I encountered an error stating: "Cannot update a component (App) while rendering a different component (UserTable). T ...

How to Customize Headers with Color and Size in MKDocs

In the following code, you'll find instructions for adjusting a dropdown menu that displays an image when expanded. The challenge is to reduce the size of the banner by half and remove the pencil icon on the left side. How can we modify the code so th ...

Glistening R resize plotOutput

Attempting to resize a plotOutput using Shiny R. The plot in question can be viewed https://i.sstatic.net/fgzag.png This is the code snippet: #In ui: fluidRow( column(width = 12, h4("Diagrama Persistencia"), plotOutput("Dia ...

What is the best way to distinguish Xpath when dealing with several elements that share identical attributes and text?

I need to find and click on the link labeled "Customer One". The current xPath I am using is: //div[contains(@class,'client-info') and contains(div/text(),'Customer')] When using Firepath, this xPath returns four separate elements (I ...

Learn how to toggle a specific div by clicking on an image generated from an ngFor loop in Angular version 11

One issue I am facing is that I have multiple sections containing image info and details. When I click on a specific image, the corresponding details should toggle. Everything works fine, however, if I click on another image without closing the previous ...

Troubleshooting: :before element not being hidden by jQuery slidetoggle()

My elements are all behaving correctly with the slidetoggle() function, except for my li:before class. I've attempted to manipulate the overflow, visibility, display, and other properties of both the :before pseudo-element and the li element, but the ...

How is it that the useEffect hook continues to "run" on the client side even when there are no dependencies, despite the fact that the page has already been pre-rendered by Next.js?

My initial assumption was that during the pre-rendering process, hooks such as useEffect would already be run on the server. I came across the concept of hydration, but I found it difficult to grasp its meaning from the blogs I read. Perhaps this could he ...

The struggle of media queries: How can you reset an element to its original display style when it's set to none?

If faced with the following scenario, how would you handle it? Imagine this: You need to hide a visible element when a specific media query is met: .myElement { display: block; } and the media query to hide it: @media (min-width: 1000px) { .myElement ...

Multer throws an error when uploading files due to an unexpected field issue

Hello, I am currently working on creating a file upload API using React and Express. To achieve this, I decided to use Muster. However, when I send an Axis request from the client, I encounter an error from the server. Error: MulterError: Unexpected fie ...

The token is present in my header, yet it keeps indicating that there is no token. Strangely, when I test it on Postman, it functions correctly. What could be causing this issue?

Here is the action code that I have implemented: export const accept = (clinicianId, duration) => (dispatch, getState) => { axios .post( "http://localhost:5000/api/patient/authAccess", clinicianId, duration, tokenCo ...