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

[data grid] Autocomplete edit input cell #4813

Open
Tracked by #9328
stamahto opened this issue May 9, 2022 · 20 comments
Open
Tracked by #9328

[data grid] Autocomplete edit input cell #4813

stamahto opened this issue May 9, 2022 · 20 comments
Labels
component: data grid This is the name of the generic UI component, not the React module! design This is about UI or UX design, please involve a designer new feature New feature or request recipe waiting for 👍 Waiting for upvotes

Comments

@stamahto
Copy link

stamahto commented May 9, 2022

Summary 💡

Should work like normal Autocomplete component. Unlike 'singleSelect', users can type in input and search options quickly.

Examples 🌈

image

Motivation 🔦

Autocomplete provides more freedom for users and also developers than select (singleSelect), like: freeSolo, getOptionLabel or multiple props.

Order ID 💳 (optional)

#31956

@stamahto stamahto added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label May 9, 2022
@alexfauquette alexfauquette added new feature New feature or request component: data grid This is the name of the generic UI component, not the React module! waiting for 👍 Waiting for upvotes and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 10, 2022
@alexfauquette
Copy link
Member

I see multiple points in this proposal:

@stamahto
Copy link
Author

I see multiple points in this proposal:

Yes, you're right. Thank you 👍
The best way is not create column type 'autocomplete', but add functionality to existing ones (i.e. getOptionLabel can be used for any column type), just add multipleSelect.

What I haven't mentioned yet is that if I don't define valueOptions for column type: single/multiSelect, I would expect default options as grouped/unique data from the current column.
This is very common in my practise, also mui-datatables do that for filter type dropdown
image

@alexfauquette
Copy link
Member

Yes, getOptionLabel could be used for both singleSelect and a multipleSelect columns. For other types I don't think so. You rarely expect to see a list of options when entering a date or a number

I'm not sure to fully understand your last point valueOptions is here to define a finite set of options the user can select. If you do not provide a valueOptions parameters, it is similar to using string column type.
I assume your expectation is the API allows you to easily get the array of unique data present in the grid such as you can make them available in an autocomplete.

But that would concern only the filtering aspect

@stamahto
Copy link
Author

stamahto commented May 12, 2022

I believe that in many apps, you always have some information according to which you want to group/filter data or just get the most data consistency (including case sensitivity) for something. Imagine, for example, a warehouse where individual items have a manufacturer, type, etc.
image
Currently, to get these values, I have to manually write for each column something like:
const manufacturers: string[] = useMemo(() => uniq(data.map(m => m.manufacturer)), [data]);

I think this is a much better way to automatically display options than as an empty field, as long as the valueOptions are not defined in the column.

I really appreciate your time, thank you.

@alexfauquette
Copy link
Member

Ok, I better understand the use case.

The technical challenge is to provide access throw the API to the list of unique values. Once done, this list of values could be reused wherever we want: a new custom renderEditCell or a filterInput, ...

@adamrneary
Copy link

@alexfauquette we have recently converted to "Pro" and have been tinkering with this problem. I think until this receives the necessary upvotes to get a canonical solution, perhaps a working example in the docs would get us there. I've managed to get us this far:

function AutocompleteEditCell<
  T extends { value: string; label: string },
  Multiple extends boolean = false,
  DisableClearable extends boolean = false,
  FreeSolo extends boolean = false
>({
  id,
  value,
  field,
  options,
  disableClearable,
  multiple,
  freeSolo,
}: GridRenderEditCellParams & {
  options: UseAutocompleteProps<
    T,
    Multiple,
    DisableClearable,
    FreeSolo
  >['options'];
  disableClearable: DisableClearable;
  multiple: Multiple;
  freeSolo: FreeSolo;
}) {
  const apiRef = useGridApiContext();
  const handleValueChange = (
    _: any,
    newValue: AutocompleteValue<T, Multiple, DisableClearable, FreeSolo>
  ) => {
    apiRef.current.setEditCellValue({
      id,
      field,
      // @ts-expect-error i can't figure out how to use AutocompleteValue
      value: typeof newValue === 'string' ? value : newValue?.value || '',
    });
  };

  return (
    <Autocomplete<T, Multiple, DisableClearable, FreeSolo>
      fullWidth
      disableClearable={disableClearable}
      multiple={multiple}
      options={options}
      freeSolo={freeSolo}
      // @ts-expect-error i can't figure out how to use AutocompleteValue
      value={options.find((o) => o.value === value)?.label || ''}
      onChange={handleValueChange}
      renderInput={(params) => <TextField {...params} />}
    />
  );
}

and this is referenced thusly:

{
    field: 'naicsCode',
    headerName: 'Industry',
    width: 250,
    editable: true,
    valueFormatter: ({ value }) => NAICS_OBJECT[value] ?? ''
    renderEditCell: (params) => (
      <AutocompleteEditCell
        {...params}
        options={NAICS_OPTIONS}
        freeSolo={false}
        multiple={false}
        disableClearable
      />
    ),  
},

First, if you see anything obvious we could be doing to clear up those two @ts-expect-errors I'd love your eyes on those! But more broadly for this task, it seems like something to this effect could go into the docs to explain how to do it manually? Similar to https://mui.com/x/react-data-grid/editing/#usage-with-mui-x-date-pickers perhaps?

@stamahto
Copy link
Author

stamahto commented Jul 20, 2022

Thanks @adamrneary for example. It's been a while so I have written solution too. Here is my example:

import { Autocomplete, TextField } from "@mui/material";
import { GridRenderEditCellParams, useGridApiContext } from "@mui/x-data-grid-pro";
import { useCallback } from "react";

interface AutocompleteEditInputCellProps {
    params: GridRenderEditCellParams,
    options: any[] | undefined,
    freeSolo?: boolean,
    multiple?: boolean,
    getOptionLabel?: (option: any) => string
}

export function AutocompleteEditInputCell(props: AutocompleteEditInputCellProps) {
    const { params, options, freeSolo, getOptionLabel, multiple } = props;
    const apiRef = useGridApiContext();

    const handleChange = useCallback((event: React.SyntheticEvent<Element, Event>, newValue: any) => {
        event.stopPropagation();
        apiRef.current.setEditCellValue({ id: params.id, field: params.field, value: newValue });
    }, [params.id, params.field]);

    const getValue = useCallback(() => {
        if (params.value)
            return params.value;

        if (multiple)
            return [];

        return null;
    }, [params.value, multiple]);

    return (
        <Autocomplete
            value={getValue()}
            onChange={handleChange}
            onInputChange={(event, value) => (freeSolo && !multiple && event) && handleChange(event, value)}
            fullWidth
            multiple={multiple}
            options={options ?? []}
            freeSolo={freeSolo}
            autoHighlight
            getOptionLabel={getOptionLabel}
            renderInput={(inputParams) => <TextField {...inputParams} error={params.error} />}
        />
    );
}

In function handleChange needs to be event.stopPropagation(); to succesfully handle selection from open list values by hitting enter, otherwise it would close edited row without value selection.

@ghost
Copy link

ghost commented Sep 8, 2022

Yes, I am using Autocomplete in 4 columns in a grid, since it is far better for large drop down lists since it allows user to search from the list by entering first few characters. Though I need single option select only.
My Upvote.

On the other note:-
I have done custom component setup as suggested in docs and similar to what @stamhato has done, and it has been working like charm till "@mui/x-data-grid-pro": "5.16.0",

When I upgrade my data-grid-pro version to 5.17.1 it starts malfunctioning, the moment the focus goes in the autocomplete cell, my processRowUpdate get triggered and the row got saved.
To save from this bug, I have reverted back to 5.16.0.

I am not sure if I should be logging a new issue for this or if this comment of mine will catch the attention of the material-ui team. Ref Order ID 💳 (optional) 28914

@alexfauquette
Copy link
Member

@adamrneary You should open another issue with a minimal reproduction of your customization that bugs in v5.17.1

Otherwise, this bug/regression will be lost into other comments asking for feature

@oliviertassinari
Copy link
Member

oliviertassinari commented Sep 9, 2022

I agree with the UX recommendation made by the author of this issue. I think that it could be great to replace most uses of Select for an Autocomplete, I assume that it would yield a better UX for end-users.

IMHO, the only question is: Should we always use the Autocomplete over the Select?
Airtable said yes. Notion said no: use a Select if there are always fewer than 5 options.

For example with the singleSelect column type edit mode: 💯 to replace the default Select to have an Autocomplete, like done by these:

  • Notion (singleSelect):

Screenshot 2022-09-09 at 17 30 45

  • Airtable (singleSelect):

Screenshot 2022-09-09 at 17 42 30

  • Google spreadsheet

Screenshot 2022-09-09 at 17 44 21

@stamahto
Copy link
Author

I agree with the UX recommendation made by the author of this issue. I think that it could be great to replace most uses of Select for an Autocomplete, I assume that it would yield a better UX for end-users.

IMHO, the only question is: Should we always use the Autocomplete over the Select? Airtable said yes. Notion said no: use a Select if there are always fewer than 5 options.

Well, I don't know the answer either, but here are some of my thoughts:

  • Does the user expect to be able to search by typing? Or what can be an advantage of not being able to write to the input.
  • What would the types for a column look like? (singleSelect, multiSelect, singleAutocomplete, multiAutocomplete)
  • Will it affect the render speed?

@AXLShorts
Copy link

I know its a lot to ask but can anyone provide me a snippet of a working autocomplete functionality inside a cell in the datagrid. I am unable to implement it

@stamahto
Copy link
Author

stamahto commented Jun 3, 2023

I know its a lot to ask but can anyone provide me a snippet of a working autocomplete functionality inside a cell in the datagrid. I am unable to implement it

My code few comments up here is updated and works pretty well: #4813 (comment)
The usage is: renderEditCell: params => <AutocompleteEditInputCell params={params} options={["A", "B"]} />

@AXLShorts
Copy link

AXLShorts commented Jun 5, 2023

I got it to work like this:

import { useState } from "react";
import { DataGrid } from "@mui/x-data-grid";

const UserPage = () => {
  const [users, setUsers] = useState([
    { id: 1, name: "John Doe", email: "[email protected]", role: "basic" },
    { id: 2, name: "Jane Smith", email: "[email protected]", role: "admin" },
    // Add more user objects as needed
  ]);

  const handleRoleChange = (event, id) => {
    const newUsers = [...users];
    const userIndex = newUsers.findIndex((user) => user.id === id);
    if (userIndex !== -1) {
      newUsers[userIndex].role = event.target.value;
      setUsers(newUsers);
      console.log(newUsers);
    }
  };

  const columns = [
    { field: "name", headerName: "Name", width: 150 },
    { field: "email", headerName: "Email", width: 200 },
    {
      field: "role",
      headerName: "Role",
      width: 120,
      type: "singleSelect",
      valueOptions: ["basic", "admin"],
      editable: true,
    },
  ];

  return (
    <div style={{ height: 400, width: "100%" }}>
      <DataGrid rows={users} columns={columns} />
    </div>
  );
};

export default UserPage;

Now it works how you would expect. The cells becomes autocomplete type elements when you start editing them. But now I'm having slight trouble in handling the change. Any idea on how to track change here?

@hcurbelo-hepyco
Copy link

Have a problem when used an Autocomplete inside DataGrid MUI
Screenshot 2023-06-11 at 19 58 57

@AXLShorts
Copy link

I gave a code snippet above. That works

@hcurbelo-hepyco
Copy link

@AXLShorts Thk, this issue is solved

@arvindkannan
Copy link

arvindkannan commented Jul 12, 2023

Have a problem when used an Autocomplete inside DataGrid MUI

Screenshot 2023-06-11 at 19 58 57

@hcurbelo-hepyco I also have same issue where in system displays [object Object] using autocomplete inside mui data grid edit mode

Update:

Was able to manage it via renderCell method to display value from object.

@oliviertassinari oliviertassinari added design This is about UI or UX design, please involve a designer and removed design: ux labels Aug 18, 2023
@aeftimia
Copy link

For what it's worth, setting renderCell and renderEditCell to the autocomplete box with editable seems to work fine for the community version for me.

@sureshvv
Copy link

I got it to work like this:

import { useState } from "react";
import { DataGrid } from "@mui/x-data-grid";

const UserPage = () => {
  const [users, setUsers] = useState([
    { id: 1, name: "John Doe", email: "[email protected]", role: "basic" },
    { id: 2, name: "Jane Smith", email: "[email protected]", role: "admin" },
    // Add more user objects as needed
  ]);

  const handleRoleChange = (event, id) => {
    const newUsers = [...users];
    const userIndex = newUsers.findIndex((user) => user.id === id);
    if (userIndex !== -1) {
      newUsers[userIndex].role = event.target.value;
      setUsers(newUsers);
      console.log(newUsers);
    }
  };

  const columns = [
    { field: "name", headerName: "Name", width: 150 },
    { field: "email", headerName: "Email", width: 200 },
    {
      field: "role",
      headerName: "Role",
      width: 120,
      type: "singleSelect",
      valueOptions: ["basic", "admin"],
      editable: true,
    },
  ];

  return (
    <div style={{ height: 400, width: "100%" }}>
      <DataGrid rows={users} columns={columns} />
    </div>
  );
};

export default UserPage;

Now it works how you would expect. The cells becomesg autocomplete type elements when you start editing them. But now I'm having slight trouble in handling the change. Any idea on how to track change here?

who is calling handleRoleChange and how does it know to di it?

@oliviertassinari oliviertassinari changed the title [DataGrid] Autocomplete edit input cell [data grid] Autocomplete edit input cell Sep 1, 2024
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! design This is about UI or UX design, please involve a designer new feature New feature or request recipe waiting for 👍 Waiting for upvotes
Projects
None yet
Development

No branches or pull requests

10 participants