1

I've made a table with a folder icon that is clickable link. To improve UX, I'd like to make the clickable area of the link bigger. I would like to make the entire contents of 3 of 4 of the cells in a row should be clickable as a link.

Here's what I mean

enter image description here

I can successfully make each cell contents (as in icons or text) clickable, but that still represents only a small area in each cell.

I've tried wrapping the entire cell contents in the link, but it leads to messy mapping, and still does not make the entire contents of the cell clickable.

Here's where I'm at so far

...

interface IProps extends Omit<unknown, 'children'> {
  folders?: IGetAllRequestDTO<IFolderDTO> | null;
  reload: boolean;
}

const RootFoldersTable = ({ folders, reload }: IProps): JSX.Element => {
  const [selectedRow, setSelectedRow] = useState('');
  const classes = useStyles();
  const dispatch = useDispatch();

  const getCorrectFormat = useCallback(cell => {
    return cell instanceof Date
      ? format(cell as Date, "MM/dd/yyyy hh:mmaaaaa'm'")
      : cell;
  }, []);

  const getAllFolders = () => {
    dispatch(appActions.getAllFoldersRequest({}));
  };

  useEffect(() => {
    getAllFolders();
  }, [reload]);

  const openDialogWithId = (folderId: string) => {
    dispatch(appActions.setDialogFormFolderId(folderId));
    dispatch(appActions.setDialogOpen());
  };

  const onDeleteFolder = (id: string) => {
    dispatch(appActions.deleteFolderRequest(id));
  };

  const tableHeadElements = [
    { label: 'Name', key: 'name', sortable: false },
    { label: 'Last Modified', key: 'updateAt', sortable: false },
    { label: 'Actions', sortable: false }
  ];

    const tableHeadElements = [
        { label: 'Name', key: 'name', sortable: false },
        { label: 'Last Modified', key: 'updateAt', sortable: false },
        { label: 'Actions', sortable: false }
      ];
    
      return (
        <div className={classes.tableContainer}>
          <TableContainer className={classes.tableBodyContainer}>
            <Table className={classes.table} size="small">
              <TableHead>
                <TableRow className={classes.tableHeadRow}>
                  {tableHeadElements.map(e => (
                    <TableCell key={e.key} align="center">
                      {e.label}
                    </TableCell>
                  ))}
                </TableRow>
              </TableHead>
        <TableBody>
          {folders?.items.map((folder: IFolderDTO, index: number) => {
            const { id, name, updatedAt } = folder;
            return (
              <TableRow
                className={classes.tableRow}
                classes={{ selected: classes.selectedRow }}
                selected={selectedRow === id}
                onClick={() => setSelectedRow(id)}
                key={index}
              >
                <Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
                  <TableCell align="center">
        
                    <IconButton color="default" size={'small'}>
                      <FolderIcon fontSize="default" />
                    </IconButton>
        
                  </TableCell>
                </Link>
                {[name, new Date(updatedAt)].map(cell => (
                  <TableCell key={index} align="center">
                    <Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
                      {getCorrectFormat(cell)}
                    </Link>
                  </TableCell>
                ))}
                <FolderActionsMenu
                  folderId={id}
                  onDeleteFolder={onDeleteFolder}
                  openDialogWithId={openDialogWithId}
                />
              </TableRow>
            );
          })}
        </TableBody>

Thanks!

elderlyman
  • 500
  • 8
  • 24

1 Answers1

1

You can create a header cell data array that describes anything you need to render the TableCell:

const headerCellData = [
  {
    name: 'Calories',
    link: '/',
  },
  {
    name: 'Fat (g)',
    link: '/?filter=fat',
    align: 'right',
  },
  {
    name: 'Carbs (g)',
    link: '/?filter=carbs',
    align: 'right',
  },
  {
    name: 'Protein (g)',
    align: 'right',
  },
];

Then map each data item to the TableCell:

<TableHead>
  <TableRow>
    {headerCellData.map((c) => (
      <TableCell align={c.align}>
        {c.link ? <Link to={c.link}>{c.name}</Link> : c.name}
      </TableCell>
    ))}
  </TableRow>
</TableHead>

make the entire contents of the cell clickable

If you want the entire cell clickable (not just the text), you need to play around with CSS a little bit. Remove the padding of TableCell where it's unclickable and set the padding of the container that holds the link to 16px which is the padding of the TableCell we just removed:

<TableCell align={c.align} sx={{ padding: 0 }}>
  <Box sx={{ padding: '16px' }}>
    {c.link ? <Link to={c.link}>{c.name}</Link> : c.name}
  </Box>
</TableCell>

Codesandbox Demo

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • Thanks for your help NearHuscarl! I looked at your codesandbox and was able to gain some insight, but - in an effort to try to keep the amount of code I used brief - I may have cut out too much context :/ I do have ```TableHead``` currently. I'll edit the sample code to add more context (and hopefully not more confusion or much more work). – elderlyman Nov 01 '21 at 21:11
  • 1
    @elderlyman but does my codesandbox work for you? You can put the text inside `Link` in your header cell and change some css to make the entire cell clickable as mentioned in my answer. what part do you still not understand or doesn't work for you here? – NearHuscarl Nov 02 '21 at 01:25
  • still working on it :) one difference between your example and my situation is that in my current situation, I'm using a function call to serve as a link to somewhere else in my app. There is an argument ```id``` to that function call that is undefined before the map method is called in the ```TableBody```. So I'm trying to figure out how to apply your technique to add a key to ```tableHeadElements``` using a function call with a dynamic argument like ```APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)``` – elderlyman Nov 02 '21 at 18:49
  • 1
    @elderlyman how did you update the id in the folder of `folders` prop? – NearHuscarl Nov 02 '21 at 23:20
  • there is a redux call to get all folders, and those are mapped through for some table rendering. But the folder id is undefined until the user selects a row, at which time the id is captured. Regarding the larger question - I'll mark your response as the answer as it is valid, even if I can't apply it in my exact case (which may be a bit too hard/long for me to capture in a good SO question). – elderlyman Nov 03 '21 at 18:15
  • I took a different approach and I'm just wrapping certain cells in a link. If you'd like to continue down the rabbit hole, I'm just stuck on styling now https://stackoverflow.com/questions/69832747/make-entire-cell-contents-clickable-material-ui-table – elderlyman Nov 03 '21 at 23:09