Search code examples
reactjsmaterial-uireact-beautiful-dnd

How to use react-beautiful-dnd with nested material-ui table


I want to use react-beautiful-dnd with nested material-ui table. I've looked online for several examples and made some mix between this https://codesandbox.io/s/qly3kkk8z4 and this https://codesandbox.io/s/react-material-ui-and-react-beautiful-dnd-uofv4?file=/src/MaterialTable.tsx:2696-2715 :


export const RatingTemplateSheetTable = ({some-props}: Props) => {

    return (
        <Table size="small">
            <TableHead>
                <TableRow>
                    <TableCell padding="checkbox">
                        <Checkbox
                            indeterminate={selected.length > 0 && selected.length < totalCount}
                            checked={selected.length > 0 && selected.length === totalCount}
                            onChange={handleSelectAllClick}
                        />
                    </TableCell>
                    <TableCell />
                    <TableCell className={classes.tableColumnHeader}>
                        <Typography variant="body1">Критерий</Typography>
                    </TableCell>
                </TableRow>
            </TableHead>
            <DragDropContext onDragEnd={handleDragEnd}>
                <Droppable droppableId={`droppableStepMain`} type="dropableStep">
                    {(provided: DroppableProvided) => (
                        <TableBody ref={provided.innerRef}>
                            {template.structure.map((block, index) => (
                                <RatingTemplateBlockRow
                                    key={block.id}
                                    index={index}
                                    createCriterionTemplate={createCriterionTemplate}
                                    createBlockTemplate={createBlockTemplate}
                                    editCriterionTemplate={editCriterionTemplate}
                                    block={block}
                                    level={0}
                                    setSelected={setSelected}
                                    selected={selected}
                                    toggleSelectedBlock={toggleSelectedBlock}
                                    uncheckAllParentBlocks={uncheckAllParentBlocks}
                                />
                            ))}
                            {provided.placeholder}
                        </TableBody>
                    )}
                </Droppable>
            </DragDropContext>
        </Table>
    )
}

export const RatingTemplateBlockRow = ({
    index,
    block,
    level,
    createBlockTemplate,
    createCriterionTemplate,
    editCriterionTemplate,
    setSelected,
    selected,
    toggleSelectedBlock,
    uncheckAllParentBlocks,
}: Props) => {

    return (
        <Draggable key={block.id} draggableId={block.id} index={index}>
            {(provided: DraggableProvided) => {
                return (
                    <React.Fragment key={index}>
                        <TableRow
                            ref={provided.innerRef}
                            {...provided.draggableProps}
                            key={index}
                            hover
                            onClick={editCriterionInternal(block)}
                            className={classNames({ [classes.upperLevelRow]: level === 0 })}
                        >
                            <TableCell className="selectCheckbox" padding="checkbox">
                                <Checkbox
                                    onClick={event => handleCheckboxClick(event)}
                                    className="selectCheckbox"
                                    checked={selected.map(s => s.id).includes(block.id)}
                                />
                            </TableCell>
                            <TableCell>
                                <div {...provided.dragHandleProps}>
                                    <FontAwesomeIcon icon={faBars} />
                                </div>
                            </TableCell>
                            <TableCell colSpan={2}>
                                <Box display="flex">
                                    <Typography
                                        variant="body2"
                                        style={{ paddingLeft: level * theme.spacing(3), fontWeight: theme.typography.fontWeightBold }}
                                    >
                                        {block.name}
                                    </Typography>
                                    <Link
                                        className={classes.addCriterionButton}
                                        onClick={createCriterionTemplateInternal(block.id, block.sheetTemplateId)}
                                    >
                                        <Typography variant="body2">Добавить критерий</Typography>
                                    </Link>
                                    <Link
                                        className={classes.addCriterionButton}
                                        onClick={createBlockTemplateInternal(block.id, block.sheetTemplateId)}
                                    >
                                        <Typography variant="body2">Добавить подблок</Typography>
                                    </Link>
                                </Box>
                            </TableCell>
                        </TableRow>
                        <Droppable droppableId={`droppableVM-${block.id}`}>
                            {provided => {
                                return (
                                    <React.Fragment>
                                        <div ref={provided.innerRef}> {/* this div ruins table */}
                                            {block.criteria.map((criterion, index) => (
                                                <RatingTemplateCriterionRow
                                                    index={index}
                                                    key={criterion.id}
                                                    level={level + 1}
                                                    editCriterionTemplate={editCriterionTemplate}
                                                    criterion={criterion}
                                                    selected={selected}
                                                    handleCriterionCheckboxClick={handleCriterionCheckboxClick}
                                                />
                                            ))}
                                            {block.children.map((childBlock, index) => (
                                                <RatingTemplateBlockRow
                                                    index={index}
                                                    createBlockTemplate={createBlockTemplate}
                                                    editCriterionTemplate={editCriterionTemplate}
                                                    createCriterionTemplate={createCriterionTemplate}
                                                    key={childBlock.id}
                                                    block={childBlock}
                                                    level={level + 1}
                                                    setSelected={setSelected}
                                                    selected={selected}
                                                    toggleSelectedBlock={toggleSelectedBlock}
                                                    uncheckAllParentBlocks={uncheckAllParentBlocks}
                                                />
                                            ))}
                                        </div>
                                        {provided.placeholder}
                                    </React.Fragment>
                                )
                            }}
                        </Droppable>
                    </React.Fragment>
                )
            }}
        </Draggable>
    )
}

export const RatingTemplateCriterionRow = ({
    criterion,
    level,
    editCriterionTemplate,
    index,
    selected,
    handleCriterionCheckboxClick,
}: Props) => {

    return (
        <Draggable key={criterion.id} index={index} draggableId={criterion.id}>
            {(provided: DraggableProvided) => {
                return (
                    <React.Fragment key={criterion.id}>
                        <TableRow
                            ref={provided.innerRef}
                            {...provided.draggableProps}
                            onClick={editCriterionInternal(criterion)}
                            className={classes.tableRow}
                        >
                            <TableCell className="selectCheckbox" padding="checkbox">
                                <Checkbox
                                    onClick={event => handleCriterionCheckboxClick(event, criterion.id)}
                                    className="selectCheckbox"
                                    checked={selected.map(s => s.id).includes(criterion.id)}
                                />
                            </TableCell>
                            <TableCell>
                                <div {...provided.dragHandleProps}>
                                    <FontAwesomeIcon icon={faBars} />
                                </div>
                            </TableCell>
                            <TableCell>
                                <Typography variant="body2" style={{ paddingLeft: level * theme.spacing(3) }}>
                                    {criterion.name}
                                </Typography>
                            </TableCell>
                        </TableRow>
                        {provided.placeholder}
                    </React.Fragment>
                )
            }}
        </Draggable>
    )
}

The main problem that I must use <React.Fragment> to wrap nested part and I need to also use div after <React.Fragment> for inner ref. This div ruins material-ui table and I can't remove it (because <React.Fragment> can't have ref). How to solve this issue?


Solution

  • Ended up with this configuration. Might rewrite to divs later, because there is an errors about tr inside div