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

feat(mui): editable Data Grid #5744 #5989

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7f4cbb7
feat(useDataGrid): enhance data grid manipulation with CRUD operation…
May 26, 2024
1fbce4e
test(useDataGrid): enhance tests with advanced filtering, pagination,…
May 26, 2024
2286c30
feat(PostList): enable title column editing in data grid
May 26, 2024
a81a401
feat(cypress-tests): add tests for cell update functionality in data …
May 26, 2024
438e415
docs(useDataGrid): update properties and add editing section
May 26, 2024
9d7b49c
chore: create changeset for editable MUI Data Grid feature
May 26, 2024
7fe92ff
feat(useDataGrid): replace useForm with useUpdate for enhanced state …
Jun 1, 2024
463c09e
refactor(useDataGrid): enhance processRowUpdate with explicit promise…
Jun 1, 2024
aa2c505
docs(useDataGrid): update documentation to reflect useUpdate integration
Jun 1, 2024
29ac185
docs(useDataGrid): enhance documentation with links and code clarity
Jun 1, 2024
f19498b
refactor(refine): streamline useDataGrid hook integration with MUI Da…
Jun 3, 2024
2a3c94b
feat(posts): integrate server-side updates in PostList data grid example
Jun 7, 2024
0467887
Merge branch 'master' into editable-MUI-data-grid
aliemir Jun 7, 2024
bb921df
feat(mui): improve typing for updateMutationOptions and useUpdate in …
Jun 13, 2024
7f6cddc
chore: merge main branch into feature branch
Jun 13, 2024
1c3a83c
fix(mui): correct import of UseUpdateProps to use import type for lin…
Jun 13, 2024
4ecd9b9
chore(editable-data-grid): update example data grid
aliemir Jun 14, 2024
a3df4e7
chore(core): export data hook props and return types
aliemir Jun 14, 2024
d7a270e
chore: add changeset
aliemir Jun 14, 2024
8cb1394
fix(use-data-grid): fix editable mutation
aliemir Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/honest-beds-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@refinedev/mui": minor
---

feat: editable feature for MUI Data Grid #5656

It is now possible to make MUI Data Grid editable by
setting editable property on specific column.

Resolves #5656
7 changes: 7 additions & 0 deletions .changeset/orange-seals-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@refinedev/core": patch
---

chore(core): add missing types of data hooks

Added missing props and return types of data hooks.
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,71 @@ const MyComponent = () => {

When the `useDataGrid` hook is mounted, it will call the `subscribe` method from the `liveProvider` with some parameters such as `channel`, `resource` etc. It is useful when you want to subscribe to live updates.

## Editing

The `useDataGrid` hook extends the editing capabilities provided by the [`<DataGrid>`](https://mui.com/x/react-data-grid/editing/) component from MUI. To enable column editing, set `editable: "true"` on specific column definitions.

`useDataGrid` leverages [`useUpdate`](https://refine.dev/docs/data/hooks/use-update/) for direct integration with update operations. This change enhances performance and simplifies the interaction model by directly using the update mechanisms provided by Refine.

Here is how you can define columns to be editable:

```tsx
const columns = React.useMemo<GridColDef<IPost>[]>(
() => [
{
field: "title",
headerName: "Title",
minWidth: 400,
flex: 1,
editable: true,
},
],
[],
);
```

### Handling Updates

With the integration of `useUpdate`, processRowUpdate from [`<DataGrid>`](https://mui.com/x/react-data-grid/editing/) directly interacts with the backend. This method attempts to update the row with the new values, handling the update logic internally.

The hook now simplifies the handling of updates by removing the need for managing form state transitions explicitly:

```tsx
const {
dataGridProps,
formProps: { processRowUpdate, formLoading },
} = useDataGrid<IPost>();
```

By default, when a cell edit is initiated and completed, the processRowUpdate function will be triggered, which will now use the mutate function from useUpdate to commit changes.

```tsx
const processRowUpdate = async (newRow: TData, oldRow: TData) => {
try {
await new Promise((resolve, reject) => {
mutate(
{
resource: resourceFromProp as string,
id: newRow.id as string,
values: newRow,
},
{
onError: (error) => {
reject(error);
},
onSuccess: (data) => {
resolve(data);
},
},
);
});
return newRow;
} catch (error) {
return oldRow;
}
};
```

## Properties

### resource
Expand Down
48 changes: 48 additions & 0 deletions examples/table-material-ui-use-data-grid/cypress/e2e/all.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,52 @@ describe("table-material-ui-use-data-grid", () => {

cy.url().should("include", "current=1");
});

it("should update a cell", () => {
cy.getMaterialUILoadingCircular().should("not.exist");

cy.intercept("/posts/*").as("patchRequest");

cy.getMaterialUIColumnHeader(1).click();

cy.get(".MuiDataGrid-cell").eq(1).dblclick();

cy.get(
".MuiDataGrid-cell--editing > .MuiInputBase-root > .MuiInputBase-input",
)
.clear()
.type("Lorem ipsum refine!")
.type("{enter}");

cy.wait("@patchRequest");

cy.get(".MuiDataGrid-cell").eq(1).should("contain", "Lorem ipsum refine!");
});

it("should not update a cell", () => {
cy.getMaterialUILoadingCircular().should("not.exist");

cy.intercept("PATCH", "/posts/*", (request) => {
request.reply({
statusCode: 500,
});
}).as("patchRequest");

cy.getMaterialUIColumnHeader(1).click();

cy.get(".MuiDataGrid-cell").eq(1).dblclick();

cy.get(
".MuiDataGrid-cell--editing > .MuiInputBase-root > .MuiInputBase-input",
)
.clear()
.type("Lorem ipsum fail!")
.type("{enter}");

cy.wait("@patchRequest");

cy.get(".MuiDataGrid-cell")
.eq(1)
.should("not.contain", "Lorem ipsum fail!");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const PostList: React.FC = () => {
const { dataGridProps } = useDataGrid<IPost>({
initialCurrent: 1,
initialPageSize: 10,
editable: true,
initialSorter: [
{
field: "title",
Expand Down Expand Up @@ -45,7 +46,13 @@ export const PostList: React.FC = () => {
type: "number",
width: 50,
},
{ field: "title", headerName: "Title", minWidth: 400, flex: 1 },
{
field: "title",
headerName: "Title",
minWidth: 400,
flex: 1,
editable: true,
},
{
field: "category.id",
headerName: "Category",
Expand Down
40 changes: 28 additions & 12 deletions packages/core/src/hooks/data/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
export { useList } from "./useList";
export { useOne } from "./useOne";
export { useMany } from "./useMany";
export { useList, UseListProps } from "./useList";
export { useOne, UseOneProps } from "./useOne";
export { useMany, UseManyProps } from "./useMany";

export { useUpdate } from "./useUpdate";
export { useCreate, UseCreateReturnType } from "./useCreate";
export { useDelete } from "./useDelete";
export { useUpdate, UseUpdateProps, UseUpdateReturnType } from "./useUpdate";
export { useCreate, UseCreateProps, UseCreateReturnType } from "./useCreate";
export { useDelete, UseDeleteProps, UseDeleteReturnType } from "./useDelete";

export { useCreateMany, UseCreateManyReturnType } from "./useCreateMany";
export { useUpdateMany } from "./useUpdateMany";
export { useDeleteMany } from "./useDeleteMany";
export {
useCreateMany,
UseCreateManyProps,
UseCreateManyReturnType,
} from "./useCreateMany";
export {
useUpdateMany,
UseUpdateManyProps,
UseUpdateManyReturnType,
} from "./useUpdateMany";
export {
useDeleteMany,
UseDeleteManyProps,
UseDeleteManyReturnType,
} from "./useDeleteMany";

export { useApiUrl } from "./useApiUrl";
export { useCustom } from "./useCustom";
export { useCustomMutation } from "./useCustomMutation";
export { useCustom, UseCustomProps } from "./useCustom";
export {
useCustomMutation,
UseCustomMutationProps,
UseCustomMutationReturnType,
} from "./useCustomMutation";

export { useDataProvider } from "./useDataProvider";
export { useInfiniteList } from "./useInfiniteList";
export { useInfiniteList, UseInfiniteListProps } from "./useInfiniteList";
2 changes: 1 addition & 1 deletion packages/core/src/hooks/data/useUpdateMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ type UpdateManyParams<TData, TError, TVariables> = {
{ ids: BaseKey[]; values: TVariables }
>;

type UseUpdateManyReturnType<
export type UseUpdateManyReturnType<
TData extends BaseRecord = BaseRecord,
TError extends HttpError = HttpError,
TVariables = {},
Expand Down
39 changes: 39 additions & 0 deletions packages/mui/src/hooks/useDataGrid/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MockJSONServer, TestWrapper } from "@test";
import { useDataGrid } from "./";
import type { CrudFilters } from "@refinedev/core";
import { act } from "react-dom/test-utils";
import { posts } from "@test/dataMocks";

describe("useDataGrid Hook", () => {
it("controlled filtering with 'onSubmit' and 'onSearch'", async () => {
Expand Down Expand Up @@ -198,4 +199,42 @@ describe("useDataGrid Hook", () => {
expect(result.current.overtime.elapsedTime).toBeUndefined();
});
});

it("when processRowUpdate is called, update data", async () => {
let postToUpdate: any = posts[0];

const { result } = renderHook(
() =>
useDataGrid({
resource: "posts",
editable: true,
}),
{
wrapper: TestWrapper({
dataProvider: {
...MockJSONServer,
update: async (data) => {
const resolvedData = await Promise.resolve({ data });
postToUpdate = resolvedData.data.variables;
},
},
}),
},
);
const newPost = {
...postToUpdate,
title: "New title",
};

await act(async () => {
if (result.current.dataGridProps.processRowUpdate) {
await result.current.dataGridProps.processRowUpdate(
newPost,
postToUpdate,
);
}
});

expect(newPost).toEqual(postToUpdate);
});
});
Loading
Loading