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.07k stars 1.26k forks source link

[data grid] Add `multipleSelect` column type #4410

Open linojon opened 2 years ago

linojon commented 2 years ago

Order ID 💳

41465

Duplicates

Latest version

The problem in depth 🔍

I have a column that contains a list of tag names. Presently I render using rowItem.tagNames.join(' '). But I also want to filter with a select list rather than simple strings. When I define the column as type: 'singleSelect' and define the valueOptions with existing tag names, this works when a row has just one tag. How can i set up the column for cells to contain more than one tag. (Filtering on just one of the tags at a time is fine).

Benchmark

Screenshot 2023-03-28 at 12 41 38 Screenshot 2023-03-28 at 12 42 23 Screenshot 2023-03-28 at 12 43 24 Screenshot 2023-03-28 at 12 43 39

Similar requests

alexfauquette commented 2 years ago

Hi, here is a draft solution. The following demo has a column 'discount' which is a single select allowing multiple tags

https://codesandbox.io/s/columntypesgrid-material-demo-forked-4bbcrv?file=/demo.js

valueFormatter: ({ value }) => (value ? value.join("/") : ""),
renderEditCell: CustomDiscountEditCell,
filterOperators: [
  {
    value: "contains",
    getApplyFilterFn: (filterItem) => {
      if (filterItem.value == null || filterItem.value === "") {
        return null;
      }
      return ({ value }) => {
        // if one of the cell values corresponds to the filter item
        return value.some((cellValue) => cellValue === filterItem.value);
      };
    },
    InputComponent: CustomFilterInputSingleSelect
  }
]
m4theushw commented 2 years ago

I added the "waiting for 👍" label. If this gets enough votes we can add a new column type based on singleSelect. The reason that singleSelect has this name is to give room for a "multiple" option. Related to https://github.com/mui/mui-x/pull/1956#discussion_r658350069

linojon commented 2 years ago

works great. Thanks! (do i leave this open?)

BTW i also needed to add a corresponding GridComparatorFn :)

alexfauquette commented 2 years ago

Yes, leave it open such that we can track upvotes or any comments.

You have to provide a custom function to sortComparator

If you need some inspiration, the default column definitions of each type are in the x-data-grid/src/codDef' folder

iddan commented 2 years ago

My team is on the Pro plan and we would really like this.

tibo-glamarche commented 7 months ago

My team and I are on the pro plan and would like to have this !

PunkFleet commented 6 months ago

Hi, here is a draft solution. The following demo has a column 'discount' which is a single select allowing multiple tags

https://codesandbox.io/s/columntypesgrid-material-demo-forked-4bbcrv?file=/demo.js

  • The valueFormatter is used to map from the array of stages to a string
  • The renderEditCell is customized to allow multiple values
  • The filter input are updated, because valueOptions are also formated, which is not the expected behavior in this particular case
  • The filter methods are updated to support columns containing an array
valueFormatter: ({ value }) => (value ? value.join("/") : ""),
renderEditCell: CustomDiscountEditCell,
filterOperators: [
  {
    value: "contains",
    getApplyFilterFn: (filterItem) => {
      if (filterItem.value == null || filterItem.value === "") {
        return null;
      }
      return ({ value }) => {
        // if one of the cell values corresponds to the filter item
        return value.some((cellValue) => cellValue === filterItem.value);
      };
    },
    InputComponent: CustomFilterInputSingleSelect
  }
]

It's very effective but doesn't work for situations with tons of options. I used autoComplete for this in v5, and now that I've updated some of the details I find that it still works:

const CategoryCell = (props: GridRenderCellParams) => {
    const {id, value, field} = props;
    const apiRef = useGridApiContext();
    const [dataSet, setDataSet] = useState<any[]>([])
    useEffect(()=>{
        const category = [
            {id:1, name: 'Technology'},
            {id:2, name: 'Business'},
            {id:3, name: 'Fintech & Finance'},
            {id:4, name: 'AI & IOT'}
        ];
        setDataSet(category);
    },[])
    const handleChange:any = async (event: SelectChangeEvent,item:any) => {
        const isValid = await apiRef.current.setEditCellValue({ id, field, value: item });
        if (isValid) {
            apiRef.current.stopCellEditMode({ id, field });
        }
    };
    const handleDelete = (index:number) => {
        const updatedValue = [...value];
        // Remove the item at the specified index
        updatedValue.splice(index, 1);
        // Update the state with the new value array
        handleChange(null, updatedValue);
    };
    return (
        <Autocomplete fullWidth value={value} multiple limitTags={3}
                      options={dataSet}
                      getOptionLabel={(option: any) => option.name}
                      filterSelectedOptions
                      onChange={handleChange}
                      renderInput={(params)=> (
                          <TextField {...params} placeholder="Choose Category" />)
                    }
                    isOptionEqualToValue={(option, value) => option.id === value.id}
                    renderTags={(value) => (
                        <Stack direction="row" spacing={1}>
                            {value.map((option,index) => (
                                <Chip
                                    key={index}
                                    label={option.name}
                                    onDelete={()=>handleDelete(index)}
                                />
                            ))}
                        </Stack>
                    )}

        />

    )
}

calling this like this:

    const editCategory: GridColDef['renderCell'] = (params) => {
        return (
            <CategoryCell {...params} />
        )
    }
    const columns: GridColDef[] = [
    { field: 'category', headerName: 'Categories', width: 400, editable: true,            
            renderCell: (params) => {
                return <Stack direction="row" spacing={1}>
                    {params.value.map((item:any)=> (
                        <Chip key={item.id} label={item.name} />
                    ))}
                </Stack>
            },
            renderEditCell: editCategory,
        },
]    
uixrob commented 5 months ago

image

+1 something like this for the data grid would increase the user experience for multi filtering on a column

victorct-tradeengage commented 1 day ago

Any update on this one?