Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGridPro] Row reorder #4034

Merged
merged 50 commits into from
Apr 22, 2022
Merged

Conversation

DanailH
Copy link
Member

@DanailH DanailH commented Feb 25, 2022

Fixes #206

Preview: https://deploy-preview-4034--material-ui-x.netlify.app/x/react-data-grid/rows/#row-reorder

Row reorder

This is a DataGridPro feature.

The approach I took is the same as the column reorder. I added a new entry in the state to track the dagging row ID. I added one callback - onRowOrderChange to match the column reordering in terms of API. The difference between the two callbacks is that I'm not calling the row to reorder one when there is sorting on filtering (thought about it but it has to be called for reach row and that is just too much, but I'm open to suggestions).
Last but not least - there is a prop on the grid itself to disable the row reorder as it is enabled by default (same as the column reorder) - disableRowReorder.

Follow up issue:


TODO:

@DanailH DanailH self-assigned this Feb 25, 2022
@DanailH DanailH added new feature New feature or request feature: Rendering layout Related to the data grid Rendering engine labels Feb 25, 2022
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 25, 2022
@github-actions
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@mui-bot
Copy link

mui-bot commented Feb 25, 2022

These are the results for the performance tests:

Test case Unit Min Max Median Mean σ
Filter 100k rows ms 246.8 431 303.2 325.4 68.27
Sort 100k rows ms 440.4 894.2 670.5 661.64 163.16
Select 100k rows ms 98.8 235.4 201.1 167.98 50.51
Deselect 100k rows ms 97.8 210.8 201.9 150.6 46.747

Generated by 🚫 dangerJS against 20b9c7e

@DanailH DanailH added component: data grid This is the name of the generic UI component, not the React module! plan: Pro Impact at least one Pro user labels Mar 2, 2022
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Mar 9, 2022
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Mar 9, 2022
@github-actions
Copy link

github-actions bot commented Mar 9, 2022

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Mar 10, 2022
@github-actions
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Mar 10, 2022
@DanailH
Copy link
Member Author

DanailH commented Apr 20, 2022

@m4theushw I've updated the demo so if it's ok I think we can merge this and have the feature as part of this week's release.

Copy link
Member

@m4theushw m4theushw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the demo so if it's ok I think we can merge this and have the feature as part of this week's release.

First, do we have enough items to release this week?

Apart from that, the feature is working correctly. 🎉 I only think the docs needs some final touches. We didn't explain how to change the icon and how to set the special __reorder__ field. Most of my suggestions for the core part are simple. We could merge this PR with the docs commented out and open another PR to refine it.

docs/data/data-grid/rows/RowOrderingGrid.tsx Outdated Show resolved Hide resolved
const [loading, setLoading] = React.useState(false);

React.useEffect(() => {
setRows(data.rows);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also set __reorder__. The ID is a hidden field in this dataset.

Here you can check if loading from useDemoData is false, then call setLoading. Also to prevent the inconsistent state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also set reorder. The ID is a hidden field in this dataset.

I didn't get that why do we need to set the special column?
For the other part of the feedback - done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was meaning this

msedge_h8kpx80D49

It shows the ID as placeholder, but the ID is not visible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still see the ID.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this demo https://deploy-preview-4034--material-ui-x.netlify.app/x/react-data-grid/rows/#row-reorder - yes. I didn't update it but I've added a section explaining how to customize it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I found weird because the ID is hidden, but if the first demo should show how the feature works with default options then makes sense to keep as it is.

packages/grid/x-data-grid/src/utils/utils.ts Outdated Show resolved Hide resolved
docs/data/data-grid/rows/rows.md Outdated Show resolved Hide resolved
docs/data/data-grid/rows/rows.md Outdated Show resolved Hide resolved
docs/data/data-grid/rows/rows.md Outdated Show resolved Hide resolved
@fourteenmeister
Copy link

Question: how i can change the width of reorder column?

Copy link
Member

@joserodolfofreitas joserodolfofreitas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can add the follow-up issue to the Row ordering doc page to see if it gets upvote traction.

@DanailH
Copy link
Member Author

DanailH commented Apr 21, 2022

Question: how i can change the width of reorder column?

@fourteenmeister I added a section in the docs regarding further customizations https://deploy-preview-4034--material-ui-x.netlify.app/x/react-data-grid/rows/#customizing-the-row-reordering-icon (it will be visible once the build is green)

@fourteenmeister
Copy link

added a section in the docs regarding further customizations https://deploy-preview-4034--material-ui-x.netlify.app/x/react-data-grid/rows/#customizing-the-row-reordering-icon (it will be visible once the build is green)

Thanks!

Copy link
Member

@cherniavskii cherniavskii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice update on docs!

docs/data/data-grid/rows/rows.md Outdated Show resolved Hide resolved
docs/data/data-grid/rows/rows.md Outdated Show resolved Hide resolved
DanailH and others added 2 commits April 22, 2022 14:53
Co-authored-by: Matheus Wichman <matheushw@outlook.com>
Co-authored-by: Matheus Wichman <matheushw@outlook.com>
@DanailH DanailH merged commit b5a7f0e into mui:master Apr 22, 2022
@DanailH
Copy link
Member Author

DanailH commented Apr 25, 2022

ezgif com-gif-maker

@oliviertassinari
Copy link
Member

oliviertassinari commented Apr 29, 2022

Happy to see this feature land! A few questions on the UI/UX of the current implementation, we might have opportunities to push it further:

  1. Why is the dragging cell not displaying the content of the first cell? I don't understand the logic behind showing the id by default over the first visible row:

Screenshot 2022-04-29 at 23 40 42

  1. From what I understand, we have two different ways to approach this problem: either move the row as dragging (e.g. AG Grid) or only as an indicator of where the cell will be once releasing the mouse (e.g. Airtable, Notion, Google Sheets, Excel)

Screenshot 2022-05-02 at 00 00 16

jQuery changed their implement a few months ago https://www.telerik.com/support/whats-new/kendo-jquery-ui:

Screenshot 2022-05-02 at 00 32 53

Since we went with the former, I guess to save time as it matches the logic of the column reorders (but with a worse UX, I would advise opening a new issue about it), would it make sense to gray it a bit the row, to help with spatial orientation?

  1. When dragging beyond the viewport, the scroll is moving very fast, too fast for me to move the row to where I target.
    As a side note, with column reordering, this is no longer working.
  2. Are we sure about the look and feel of the drag button? I would have personally expected a small icon, maybe 18px, and to use the theme.palette.action.active design token like the IconButton: https://mui.com/material-ui/react-button/#icon-button.

Current
Screenshot 2022-04-29 at 23 55 25

Proposal
Screenshot 2022-04-29 at 23 55 19

@DanailH
Copy link
Member Author

DanailH commented May 2, 2022

Thanks for the feedback @oliviertassinari.
Regarding your first question - @mbrookes had a similar one raised here #4034 (comment). Basically, I chose the ID because if you reorder the first column then the content will change. That's why I've also added an example explaining how to change the value that is being displayed. Another option of the API was to mark which cell should be displayed there but I feel that the way it is currently built allows for more customizations.
Regarding showing the entire row when dragging - it will create problems when virtualized columns.

For the second question - I agree - there is room for improvement. I did it to match the same behavior we have for the columns. We can create a ticket to update both to match.
PS: I also noticed that dragging a column doesn't move the scroll left or right if it is done over the columns container

@oliviertassinari
Copy link
Member

oliviertassinari commented May 2, 2022

  1. Would it be possible to display the content of the first column, or to concatenate the content of the first few columns? I think that showing an illustration for the row rather than the ID would already be a step forward
  2. Yeah ok, makes sense, it's what I assumed. Could you or @joserodolfofreitas open a new issue? I do think that we should change this behavior once we find the time (meaning get important enough, unlikely anytime soon)
  3. Sounds great, a new issue would help.
  4. Would this work as a quick follow-up?

@mbrookes
Copy link
Member

mbrookes commented May 2, 2022

I had added some further feedback there this evening, but hadn't seen @oliviertassinari's comments. It seems we share some of the same concerns, but I also raised some additional ones. Copied below, but see linked discussion for context.

if there are a lot of columns it just looks ugly

I noticed from the benchmark examples that only one attempts to do this, and even then is just showing the data, not the row, so the approach of using a single cell makes sense.

For the default, whichever is logically first whether pinned or dragged there would be fine. And to override, allowing the developer to specify a column by name rather than having to provide a value for each row would be far preferable, although the latter option could still be retained.

Regarding the UX, the other issue I noticed is with the row highlight. If it's a selected row that's being dragged, the blue highlight follows the row being moved, but if a row is grey (mouse-over?), it doesn't, but rather moves to seemingly random rows if you scroll past the currently displayed rows. You can see it in the recording used in the Twitter post. If needed we can jump on a screens-share so I can demonstrate.

For the visual appearance of the table when dragging, when I looked at the benchmarks, I noticed that AG Grid and one other implementation use a transition that makes it very obvious what's happening, but a common pattern is to show a line at the insertion point. Not a huge fan of that, but it's clearer than the current implementation. Having the moved row clearly indicated (as it is when selected before being moved) works too. Perhaps highlight the row being moved in blue, even if it isn't selected, then remove the highlight when the moved row is "dropped"?

I wasn't too sure about the green plus icon on the cursor, since nothing is being added (appreciate this was copied from column reordering). Perhaps "grab" and grabbing" would be better? (Not 100% sure about that either...)

And finally, the Material DragIndicator icon is quite "heavy". Perhaps it could be rendered in a lighter shade, and/or swapped with DragHandle?

@DanailH
Copy link
Member Author

DanailH commented May 3, 2022

Thanks for the feedback @oliviertassinari and @mbrookes. I'll organize it into a separate issue today

@DanailH
Copy link
Member Author

DanailH commented May 4, 2022

Here is the follow up issue #4754

alexfauquette pushed a commit to alexfauquette/mui-x that referenced this pull request Aug 26, 2022
@oussamachah2020
Copy link

hello, m having an issue here.
image

after adding a new record, the drag button become disabled even if the table is on mode view. i need to reload the page to get it to work

any solution please i need asap

here is my code

import * as React from "react";
import { DataGridPro, GridRowOrderChangeParams } from "@mui/x-data-grid-pro";
import {
  createNewEtape,
  fetchEtapes,
  getGreatestRank,
  updateEtapeByDragAndDrop,
} from "../Loaders/etape";
import { client } from "../lib/appwrite";
import {
  GridColDef,
  GridEventListener,
  GridRowEditStopReasons,
  GridRowId,
  GridRowModes,
  GridRowModesModel,
  GridRowsProp,
} from "@mui/x-data-grid";
import * as moment from "moment-timezone";
import { Etape } from "../types/etapeType";
// import SaveIcon from "@mui/icons-material/Save";
// import CancelIcon from "@mui/icons-material/Close";
import { Autocomplete, Button, TextField } from "@mui/material";
import { randomId } from "@mui/x-data-grid-generator";
import AddIcon from "@mui/icons-material/Add";
import ChoiceInput from "./ChoiceInput";
import { db } from "../../firebase";
import { collection, onSnapshot, query } from "firebase/firestore";

function updateRowPosition(
  initialIndex: number,
  newIndex: number,
  rows: Array<Etape>
) {
  console.log(initialIndex, newIndex);

  const rowsClone = [...rows];
  const row = rowsClone.splice(initialIndex, 1)[0];
  rowsClone.splice(newIndex, 0, row);

  return rowsClone;
}

interface EditToolbarProps {
  onClick: () => void;
}

function EditToolbar(props: EditToolbarProps) {
  return (
    <Button color="primary" startIcon={<AddIcon />} onClick={props.onClick}>
      Add record
    </Button>
  );
}

export default function RowOrderingGrid() {
  const [rows, setRows] = React.useState<Etape[]>([]);
  const [loading, setLoading] = React.useState(false);
  const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>(
    {}
  );

  const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => {
    setRowModesModel(newRowModesModel);
  };

  const handleRowEditStop: GridEventListener<"rowEditStop"> = (
    params,
    event
  ) => {
    if (params.reason === GridRowEditStopReasons.rowFocusOut) {
      event.defaultMuiPrevented = true;
    }
  };

  const handleSaveClick = (id: string) => () => {
    setRowModesModel((prev) => ({
      ...prev,
      [id]: { mode: GridRowModes.View },
    }));
  };

  const handleRankChange = async () => {
    let new_rank: number;

    const data = await getGreatestRank();

    if (data.length == 0) {
      new_rank = 1;
    } else {
      new_rank = data[0].rank + 1;
    }

    return new_rank;
  };

  const AddNewRecord = () => {
    const id = randomId();
    const now = moment().tz("America/New_York");

    setRows((oldRows) => [
      ...oldRows,
      {
        id,
        titre: "",
        id_code: "",
        date_creation: now.format(),
        derniere_modification: now.format(),
        choix: [],
        rank: 0,
        isNew: true,
      },
    ]);
    setRowModesModel((oldModel) => ({
      ...oldModel,
      [id]: { mode: GridRowModes.Edit, fieldToFocus: "titre" },
    }));
  };

  const createEtape = async (updatedRow: Etape) => {
    setLoading(true);
    const new_rank_value = await handleRankChange();

    createNewEtape(updatedRow.titre, updatedRow.id_code, new_rank_value)
      .then(() => {
        console.log("ok");
        console.log(rowModesModel[updatedRow.id]?.mode);
      })
      .catch((err) => console.error(err))
      .finally(() => {
        setLoading(false);
      });
  };

  function loadFromBackend() {
    fetchEtapes()
      .then((res: Etape[]) => {
        setRows(res.sort((a, b) => a.rank - b.rank));
      })
      .catch((err) => {
        console.error(err);
      });
  }

  React.useEffect(() => {
    loadFromBackend();
  }, []);

  React.useEffect(() => {
    const q = query(collection(db, "etape"));
    const unsubscribe = onSnapshot(q, (querySnapshot) => {
      querySnapshot.forEach((doc) => {
        if (doc.exists()) {
          loadFromBackend();
        }
      });
    });

    return () => {
      unsubscribe();
    };
  }, []);

  const handleRowOrderChange = async (params: GridRowOrderChangeParams) => {
    setLoading(true);
    const { oldIndex, targetIndex } = params;

    const newRows = updateRowPosition(oldIndex, targetIndex, rows);

    for (let i = 0; i <= newRows.length - 1; i++) {
      try {
        await updateEtapeByDragAndDrop({ ...newRows[i], rank: i + 1 });
      } catch (error) {
        console.error(error);
      }
    }

    setLoading(false);
  };

  const columns: GridColDef<any>[] = [
    { field: "titre", headerName: "Titre", flex: 1, editable: true },
    {
      field: "date_creation",
      headerName: "Date de création",
      renderCell(params) {
        return (
          <p>{`${moment(params.value).format("YYYY-MM-DD")} - ${moment(
            params.value
          ).format("HH:mm:ss")}`}</p>
        );
      },
    },
    {
      field: "derniere_modification",
      headerName: "Dérniere modification",
      flex: 1,
      renderCell(params) {
        return (
          <p>{`${moment(params.value).format("YYYY-MM-DD")} - ${moment(
            params.value
          ).format("HH:mm:ss")}`}</p>
        );
      },
    },
    {
      field: "id_code",
      headerName: "ID Code",
      flex: 1,
      editable: true,
    },
    // {
    //   field: "choix",
    //   headerName: "Choix",
    //   flex: 1,
    //   renderCell(params) {
    //     return <ChoiceInput docId={params.row.id} />;
    //   },
    // },
  ];

  return (
    <div style={{ height: 500, width: "100%" }}>
      <DataGridPro
        loading={loading}
        rows={rows}
        columns={columns}
        rowReordering
        editMode="row"
        rowModesModel={rowModesModel}
        onRowModesModelChange={handleRowModesModelChange}
        onRowEditStop={handleRowEditStop}
        getRowId={(row) => row.id}
        pagination
        pageSizeOptions={[5, 10]}
        processRowUpdate={(updatedRow, originalRow) => {
          createEtape(updatedRow);
          // handleSaveClick(originalRow.id);
        }}
        onRowOrderChange={handleRowOrderChange}
        slots={{
          toolbar: EditToolbar,
        }}
        slotProps={{
          toolbar: { onClick: () => AddNewRecord() },
        }}
      />
    </div>
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! feature: Rendering layout Related to the data grid Rendering engine new feature New feature or request plan: Pro Impact at least one Pro user
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[data grid] Implement Row reorder