1
votes

The following sandbox shows code that works perfectly well for a Material UI Table placed within a Paper component with a sticky header: https://codesandbox.io/s/2n40y

I would like the scrollbars (horizontal and vertical) to appear at the edges of the browser page, bottom and right respectively, yet still control the scrolling of the Table itself.

Currently I am only able to do this when removing the table from the Paper and making it a child of the page directly -- or a child of a main div that would span the full page height. Yet I need the Paper component to remain there, where other components will be placed above and below it.

Any ideas on how to achieve this?

UPDATE: in the attached sketch, the browser border appears in black and the scrollbars where they should ideally be appear in green. There is a container div in the middle of the page that contains the table in red. The table's headers should be sticky and the table shouldn't appear beyond the container div which acts as an aesthetic wrapper around it. Ideally, the browser vertical scrollbar would scroll the whole page down while leaving the page header (title + subtitle) and the table headers sticky. Also, when horizontally scrolling, the table should scroll within the container div. This is why I marked the parts that should not ideally appear in dashed lines.

enter image description here

3
Remove style={{ height: "400px" }} demo.js and it will have full height. If this is not an issue can you explain it better? - Veselin Kontić
Thank you @VeselinKontić -- but no, if I do this: (1) the sticky header doesn't stick anymore; and (2) the horizontal scroll is still really within the Paper object and not on the bottom of the page, full width. I hope it's clearer? - Sammy

3 Answers

2
votes

All the changes we need to make are on demo.js

Firstly, we need to use a custom MUI TableContainer as the containerComponent for your @devexpress/dx-react-grid-material-ui Table. Basically, the purpose of this is so we can remove the overflow properties of the Table so that the scrolling is primarily for the body to address the requirement of

the scrollbars (horizontal and vertical) to appear at the edges of the browser page, bottom and right respectively

import TableContainer from "@material-ui/core/TableContainer";
import makeStyles from "@material-ui/core/styles/makeStyles";

const useStyles = makeStyles({
  tableContainer: {
    overflow: "initial"
  }
});

const MUITableContainer = ({ children, ...rest }) => {
  const classes = useStyles();
  return (
    <TableContainer classes={{ root: classes.tableContainer }} {...rest}>
      {children}
    </TableContainer>
  );
};

Secondly, at your MUITableContainer, get rid of the height: 400px so that the table's height will respond to the content. Again, the browser body bottom & right scrollbars will now control the document's scroll positions - this includes the table. Take a look at the Table containerComponent prop as well - we have assigned the custom TableContainer we created earlier.

<Paper>
  <Grid rows={rows} columns={columns} rootComponent={GridRoot}>
    <Table
      containerComponent={MUITableContainer}
      tableComponent={StickyTable}
    />
    ...

Lastly, to test this requirement:

there will be other objects before and after it.

Just render sample components before & after the Paper component

Edit Table Sticky Header (forked)

1
votes

I cannot see the design spec you have referenced, but going off the sample in the comment on the first answer, I think you are trying to keep the header and footer from going off the left edge of the viewport when the user scrolls. But using just CSS, no JavaScript.

I took the entirety of the HTML (only the HTML) from your original code at https://codesandbox.io/s/2n40y (specifically <div id="root">) and dropped that into a Codepen.

Then I added a few visual design styles from your example so it looks kinda close.

Then I added the following CSS to make the column headers sticky:

th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

I dropped in the following HTML as the first child and last child of <div id="root">, though it really doesn't matter where they live as long as they are not in the table.

  <div class="stuckHeaderFooter">
    [...]
  </div>

To keep those from scrolling off screen when I scroll to the right, I made them sticky to the left (for RTL content you would need to make it sticky to the right):

.stuckHeaderFooter {
  position: -webkit-sticky;
  position: sticky;
  left: 0;
  padding: 0 1em;
  display: inline-block;
}

The padding just makes it look less ugly. The inline-block is there to keep the block-level elements from filling the entire document width, which would keep them from being properly sticky since they would be wider than the viewport. You will probably have to set a max-width using vw units, but without knowing your content, target sizes, etc., I cannot say what would be best.

Finally, you have to remove the inline height: 400px on the first <div> under the root (<div class="MuiPaper-root MuiPaper-elevation1 MuiPaper-rounded" style="height: 400px;">). If you cannot override it because something injects it, then this style will override it for you but it brittle without knowing what else may be going on:

#root > div {
  height: auto !important;
}

CSS only, no JavaScript in this approach at all, which is what I think you wanted.

Update: 5 October 2020: Looking at the sketch provided with the question, it is important to note that the container that does the clipping is the one that gets the scrollbar. So your options for a CSS-only solution are limited:

  • Add a fake block to make a visible border on the right (I updated the pen to add one; look at #root::after for an example but you will still need JS to make sure that does not appear until the table starts to get covered).
  • Yeah, I ran out of ideas.

In conclusion, I don't believe there is a CSS-only solution here because of how clipped areas work.

0
votes

You can achieve this by setting the height and width to be equal to the viewport:

<Paper style={{ height: "100vh", width: "100vw" }}>

Example modified: https://codesandbox.io/s/table-sticky-header-forked-l77is?file=/demo.js