mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.12k stars 1.28k forks source link

[data grid] Implementing React-Beautiful-DnD with DataGridPro #6008

Open robcecil opened 2 years ago

robcecil commented 2 years ago

Order ID πŸ’³

43370

Duplicates

Latest version

The problem in depth πŸ”

I wish to integrate a third party DnD library (React-beautiful-Dnd - 'RBDND') with MUI. I have used both MUI v4 and the RBDND library in the past with List and other MUI components with great success. Currently I am trying to be able to:

When I run my test project, I am getting a warning in console from RBDND:

react-beautiful-dndA setup problem was encountered.> Invariant failed: provided.innerRef has not been provided with a HTMLElement.You can find a guide on using the innerRef callback functions at:https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/using-inner-ref.md
...

Also, when I attempt a drag operation, I get the following warning, which stems from MUI:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `Draggable`.
    at GridRow (http://localhost:3000/static/js/vendors~main.chunk.js:85968:5)
    at Draggable (http://localhost:3000/static/js/vendors~main.chunk.js:132146:65)
    at ConnectFunction (http://localhost:3000/static/js/vendors~main.chunk.js:161881:75)
    at PrivateDraggable (http://localhost:3000/static/js/vendors~main.chunk.js:132499:26)
    at PublicDraggable (http://localhost:3000/static/js/vendors~main.chunk.js:132510:32)
    at DraggableGridRow (http://localhost:3000/static/js/main.chunk.js:75:27)
    ...

I am wrapping my DataGridPro instance in a Droppable :

        <Droppable droppableId="droppable-datagrid" isDropDisabled>
          {(provided, snapshot) => (
            <DataGridPro
              sx={{ minHeight: '400px' }}
              columns={[...gridColumns]}
              rows={questions}
              getRowId={gridRow => gridRow.id}
              hideFooter
              density="standard"
              components={{ Row: DraggableGridRow }}
              disableSelectionOnClick
              {...provided.droppableProps}
              ref={provided.innerRef}
            />
          )}
        </Droppable>

Even though I do not intend to drag and drop rows within the grid, the rule from RBDND is that all Draggables must be enclosed in a Droppable (hence the isDropDisabled). That is why the DataGridPro must be enclosed in a Droppable .

Next, I've implemented a DraggableGridRow thusly:

const DraggableGridRow = (props: GridRowProps) => {
  return (
    <Draggable draggableId={`${props.rowId}`} index={props.index}>
      {(provided, snapshot) => {
        return <GridRow {...props} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} />
      }}
    </Draggable>
  )
}

Not shown here (but in the attached zip file), is wrapping Tab instances each in Droppable wrappers.

The use case is to drag a row out of the grid and drop onto individual DroppableTab components (and yes, I don't need the Tabs container to be droppable, only the individual tabs themselves).

I've gotten a similar Proof of concept working fine between MUI TableRow and MUI Tab fine. But my difficulty is specifically with the MUI-X DataGridPro and MUI Tab.

thanks

dnd.zip

Your environment 🌎

System: OS: Windows 10 10.0.19044 Binaries: Node: 16.13.1 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.15 - C:\Program Files\nodejs\yarn.CMD npm: 8.1.2 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: 104.0.5112.102 Edge: Spartan (44.19041.1266.0), Chromium (104.0.1293.70)
m4theushw commented 2 years ago

I created a CodeSandbox to make easier to see the error messages: https://codesandbox.io/s/trusting-benz-yxpr23?file=/demo.tsx

There're a few tasks we need to do to support React-Bautiful-Dnd as well as other libs that pass custom props and need access to refs to internal components:

  1. ~GridRow is not forwarding the received ref to the root element. This explains one of the "provided.innerRef has not been provided with a HTMLElement." errors. The following diff fixes this problem:~ (fixed by https://github.com/mui/mui-x/issues/6008)

    diff --git a/packages/grid/x-data-grid/src/components/GridRow.tsx b/packages/grid/x-data-grid/src/components/GridRow.tsx
    index 2a3d2fbf9..4e79c1331 100644
    --- a/packages/grid/x-data-grid/src/components/GridRow.tsx
    +++ b/packages/grid/x-data-grid/src/components/GridRow.tsx
    @@ -1,7 +1,7 @@
     import * as React from 'react';
     import PropTypes from 'prop-types';
     import clsx from 'clsx';
    -import { unstable_composeClasses as composeClasses } from '@mui/material';
    +import { unstable_composeClasses as composeClasses, useForkRef } from '@mui/material';
     import { GridRowEventLookup } from '../models/events';
     import { GridRowId, GridRowModel } from '../models/gridRows';
     import {
    @@ -91,7 +91,10 @@ const EmptyCell = ({ width }: { width: number }) => {
       return <div className="MuiDataGrid-cell" style={style} />; // TODO change to .MuiDataGrid-emptyCell or .MuiDataGrid-rowFiller
     };
    
    -function GridRow(props: React.HTMLAttributes<HTMLDivElement> & GridRowProps) {
    +const GridRow = React.forwardRef<
    +  HTMLDivElement,
    +  React.HTMLAttributes<HTMLDivElement> & GridRowProps
    +>(function GridRow(props, refProp) {
       const {
         selected,
         rowId,
    @@ -124,6 +127,7 @@ function GridRow(props: React.HTMLAttributes<HTMLDivElement> & GridRowProps) {
       const sortModel = useGridSelector(apiRef, gridSortModelSelector);
       const treeDepth = useGridSelector(apiRef, gridRowTreeDepthSelector);
       const headerGroupingMaxDepth = useGridSelector(apiRef, gridDensityHeaderGroupingMaxDepthSelector);
    +  const handleRef = useForkRef(ref, refProp);
    
       const ariaRowIndex = index + headerGroupingMaxDepth + 2; // 1 for the header row and 1 as it's 1-based
       const { hasScrollX, hasScrollY } = apiRef.current.getRootDimensions() ?? {
    @@ -472,7 +476,7 @@ function GridRow(props: React.HTMLAttributes<HTMLDivElement> & GridRowProps) {
    
       return (
         <div
    -      ref={ref}
    +      ref={handleRef}
           data-id={rowId}
           data-rowindex={index}
           role="row"
    @@ -487,7 +491,7 @@ function GridRow(props: React.HTMLAttributes<HTMLDivElement> & GridRowProps) {
           {emptyCellWidth > 0 && <EmptyCell width={emptyCellWidth} />}
         </div>
       );
    -}
    +});
    
     GridRow.propTypes = {
       // ----------------------------- Warning --------------------------------
  2. Another "provided.innerRef has not been provided with a HTMLElement." message is because the ref passed to DataGridProp is not being forwarded to a HTML element since the first render. It's forwarded to a HTML element but initially this ref is null because there's a logic do detect if the component is being rendered by SSR and render nothing.

    https://github.com/mui/mui-x/blob/1cd772898ca797597053d7e645312f36bc6514bf/packages/grid/x-data-grid/src/components/containers/GridRoot.tsx#L75-L89

    If the React.useState(false) above is initialized with true the error is gone. Since we can't change the default value, I propose to add a disableSSR prop to skip this SSR logic and render the component immediadiatelly.

  3. ~Doing these changes still leaves us with the "Cannot find droppable entry with id [droppable-datagrid]" error. This happens because provided.droppableProps passes custom props to the grid, e.g. data-rbd-droppable-id, and these props are not forwarded to the root element. To solve that we need to get all props not used by the grid and spread them to GridRoot, more or less like below but with all props:~ (fixed by #8845)

    diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
    index 5af063bfe..a58f87c74 100644
    --- a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
    +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
    @@ -24,13 +24,13 @@ const DataGridProRaw = React.forwardRef(function DataGridPro<R extends GridValid
       inProps: DataGridProProps<R>,
       ref: React.Ref<HTMLDivElement>,
     ) {
    -  const props = useDataGridProProps(inProps);
    +  const { disableColumnMenu, ...rest } = useDataGridProProps(inProps);
       const apiRef = useDataGridProComponent(props.apiRef, props);
       useLicenseVerifier('x-data-grid-pro', releaseInfo);
    
       return (
         <GridContextProvider apiRef={apiRef} props={props}>
    -      <GridRoot className={props.className} style={props.style} sx={props.sx} ref={ref}>
    +      <GridRoot className={props.className} style={props.style} sx={props.sx} ref={ref} {...rest}>
             <GridErrorHandler>
               <GridHeaderPlaceholder />
               <GridBody
erezleviim commented 11 months ago

Hi, I want to use react-beautiful-dnd in Mui x Datagrid (premium). It's quite problematic because Datagrid needs to be wrapped in div (that gets the ref and droppableProps), and also in Dropabble. I tried also to create custom grid row (instead of the original one).

Can someone help please? Working solution example can be also helpful.

Thanks

herobaby71 commented 6 months ago

Did you guys end up resolving the issue? I am encountering the same problem.

EkaLinMan commented 5 months ago

Hi @robcecil, did you find a way to integrate React-Beautiful-DnD with DataGridPro? or any alternative way to do D&D?