diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js
deleted file mode 100644
index 53ee4312c7d4..000000000000
--- a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as React from 'react';
-import { DataGrid } from '@mui/x-data-grid';
-import { useDemoData } from '@mui/x-data-grid-generator';
-
-const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin'];
-
-export default function UseNonNativeSelect() {
- const { data } = useDemoData({
- dataSet: 'Employee',
- visibleFields: VISIBLE_FIELDS,
- rowLength: 100,
- });
-
- return (
-
-
-
- );
-}
diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx
deleted file mode 100644
index 53ee4312c7d4..000000000000
--- a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as React from 'react';
-import { DataGrid } from '@mui/x-data-grid';
-import { useDemoData } from '@mui/x-data-grid-generator';
-
-const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin'];
-
-export default function UseNonNativeSelect() {
- const { data } = useDemoData({
- dataSet: 'Employee',
- visibleFields: VISIBLE_FIELDS,
- rowLength: 100,
- });
-
- return (
-
-
-
- );
-}
diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview
deleted file mode 100644
index f042b7c7db38..000000000000
--- a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview
+++ /dev/null
@@ -1,8 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/data/data-grid/filtering-recipes/filtering-recipes.md b/docs/data/data-grid/filtering-recipes/filtering-recipes.md
index 6ff6d103ae59..02e7115a12b7 100644
--- a/docs/data/data-grid/filtering-recipes/filtering-recipes.md
+++ b/docs/data/data-grid/filtering-recipes/filtering-recipes.md
@@ -13,9 +13,3 @@ Currently if you want to use the [Quick filter](/x/react-data-grid/filtering/qui
A common use case is to have certain components positioned outside of the grid. Because of the way the grid context works this might not be a straightforward thing to do. The example below illustrates how this use case can be achieved.
{{"demo": "QuickFilterOutsideOfGrid.js", "bg": "inline", "defaultCodeOpen": false}}
-
-## Use non-native select in filter panel
-
-If you do not want to use the native select in the filtering panel you can switch it to the `@mui/material/Select` component by using the `slotProps` property.
-
-{{"demo": "UseNonNativeSelect.js", "bg": "inline", "defaultCodeOpen": false}}
diff --git a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md
index fd5f2221b27f..f0c60e7c976a 100644
--- a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md
+++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md
@@ -77,6 +77,26 @@ Below are described the steps you need to make to migrate from v6 to v7.
- The deprecated props `components` and `componentsProps` have been removed. Use `slots` and `slotProps` instead. See [components section](/x/react-data-grid/components/) for more details.
- The `slots.preferencesPanel` slot and the `slotProps.preferencesPanel` prop were removed. Use `slots.panel` and `slotProps.panel` instead.
+- The `getOptionValue` and `getOptionLabel` props were removed from the following components:
+
+ - `GridEditSingleSelectCell`
+ - `GridFilterInputSingleSelect`
+ - `GridFilterInputMultipleSingleSelect`
+
+ Use the `getOptionValue` and `getOptionLabel` properties on the `singleSelect` column definition instead:
+
+ ```tsx
+ const column: GridColDef = {
+ type: 'singleSelect',
+ field: 'country',
+ valueOptions: [
+ { code: 'BR', name: 'Brazil' },
+ { code: 'FR', name: 'France' },
+ ],
+ getOptionValue: (value: any) => value.code,
+ getOptionLabel: (value: any) => value.name,
+ };
+ ```
### State access
@@ -208,6 +228,8 @@ Below are described the steps you need to make to migrate from v6 to v7.
| `unstable_gridHeaderFilteringStateSelector` | `gridHeaderFilteringStateSelector` |
| `unstable_gridTabIndexColumnHeaderFilterSelector` | `gridTabIndexColumnHeaderFilterSelector` |
+- The filter panel no longer uses the native version of the [`Select`](https://mui.com/material-ui/react-select/) component for all components.
+
diff --git a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx
index 60fb45c1802e..0075fab62b38 100644
--- a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx
+++ b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx
@@ -13,6 +13,7 @@ import {
getColumnHeadersTextContent,
getColumnValues,
getCell,
+ getSelectByName,
} from 'test/utils/helperFn';
import { expect } from 'chai';
import {
@@ -2121,7 +2122,7 @@ describe(' - Row grouping', () => {
/>,
);
- fireEvent.change(screen.getByRole('combobox', { name: 'Operator' }), {
+ fireEvent.change(getSelectByName('Operator'), {
target: { value: '>' },
});
fireEvent.change(screen.getByRole('spinbutton', { name: 'Value' }), {
@@ -2147,7 +2148,7 @@ describe(' - Row grouping', () => {
/>,
);
- fireEvent.change(screen.getByRole('combobox', { name: 'Operator' }), {
+ fireEvent.change(getSelectByName('Operator'), {
target: { value: '>' },
});
fireEvent.change(screen.getByRole('spinbutton', { name: 'Value' }), {
@@ -2375,7 +2376,7 @@ describe(' - Row grouping', () => {
/>,
);
- fireEvent.change(screen.getByRole('combobox', { name: 'Operator' }), {
+ fireEvent.change(getSelectByName('Operator'), {
target: { value: '>' },
});
fireEvent.change(screen.getByRole('spinbutton', { name: 'Value' }), {
@@ -2402,7 +2403,7 @@ describe(' - Row grouping', () => {
/>,
);
- fireEvent.change(screen.getByRole('combobox', { name: 'Operator' }), {
+ fireEvent.change(getSelectByName('Operator'), {
target: { value: '>' },
});
fireEvent.change(screen.getByRole('spinbutton', { name: 'Value' }), {
diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx
index 5cb84a07d210..09fad0bdbba0 100644
--- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx
+++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx
@@ -141,8 +141,10 @@ const GridHeaderFilterCell = React.forwardRef {
if (!hasFocus) {
- if (inputRef.current && inputRef.current.contains(event.target as HTMLElement)) {
+ if (inputRef.current?.contains?.(event.target as HTMLElement)) {
inputRef.current.focus();
}
apiRef.current.setColumnHeaderFilterFocus(colDef.field, event);
diff --git a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx
index 15f9d1928c5a..abeab01c4098 100644
--- a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx
+++ b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx
@@ -531,22 +531,6 @@ describe(' - Edit components', () => {
});
});
- it('should apply getOptionLabel to the options provided', () => {
- defaultData.columns[0].renderEditCell = (params) => {
- return renderEditSingleSelectCell({
- ...params,
- getOptionLabel: (value) => (value as string).toLowerCase(),
- });
- };
- render();
-
- const cell = getCell(0, 0);
- fireEvent.doubleClick(cell);
-
- expect(screen.queryAllByRole('option')[0]).to.have.text('nike');
- expect(screen.queryAllByRole('option')[1]).to.have.text('adidas');
- });
-
it('should pass the value prop to the select', async () => {
render();
diff --git a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx
index 0e837228980e..6427e742118f 100644
--- a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx
+++ b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx
@@ -19,7 +19,7 @@ import { createRenderer, fireEvent, screen, act, within } from '@mui-internal/te
import { expect } from 'chai';
import * as React from 'react';
import { spy } from 'sinon';
-import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn';
+import { getColumnHeaderCell, getColumnValues, getSelectInput } from 'test/utils/helperFn';
const SUBMIT_FILTER_STROKE_TIME = DATA_GRID_PRO_PROPS_DEFAULT_VALUES.filterDebounceMs;
@@ -170,10 +170,10 @@ describe(' - Filter', () => {
/>,
);
- const selectListOfColumns = document.querySelectorAll(
- '.MuiDataGrid-filterFormColumnInput',
- )[0];
- const availableColumns = within(selectListOfColumns).getAllByRole('option');
+ const select = screen.getByRole('combobox', { name: 'Columns' });
+ fireEvent.mouseDown(select);
+ const listbox = screen.getByRole('listbox', { name: 'Columns' });
+ const availableColumns = within(listbox).getAllByRole('option');
expect(availableColumns.length).to.equal(1);
});
@@ -437,10 +437,12 @@ describe(' - Filter', () => {
// The first combo is hidden and we include hidden elements to make the query faster
// https://github.com/testing-library/dom-testing-library/issues/820#issuecomment-726936225
- const select = screen.queryAllByRole('combobox', { name: 'Logic operator', hidden: true })[
- isJSDOM ? 1 : 0 // https://github.com/testing-library/dom-testing-library/issues/846
- ];
- fireEvent.change(select, { target: { value: 'or' } });
+ const input = getSelectInput(
+ screen.queryAllByRole('combobox', { name: 'Logic operator', hidden: true })[
+ isJSDOM ? 1 : 0 // https://github.com/testing-library/dom-testing-library/issues/846
+ ],
+ );
+ fireEvent.change(input!, { target: { value: 'or' } });
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[1].reason).to.equal('changeLogicOperator');
expect(getColumnValues(0)).to.deep.equal([]);
diff --git a/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx b/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx
index 0a4514541b3b..44f13cc77952 100644
--- a/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx
+++ b/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx
@@ -2,7 +2,10 @@ import { GRID_STRING_COL_DEF } from './gridStringColDef';
import { GridSingleSelectColDef, ValueOptions } from '../models/colDef/gridColDef';
import { renderEditSingleSelectCell } from '../components/cell/GridEditSingleSelectCell';
import { getGridSingleSelectOperators } from './gridSingleSelectOperators';
-import { isSingleSelectColDef } from '../components/panel/filterPanel/filterPanelUtils';
+import {
+ getValueOptions,
+ isSingleSelectColDef,
+} from '../components/panel/filterPanel/filterPanelUtils';
import { isObject } from '../utils/utils';
const isArrayOfObjects = (options: any): options is Array> => {
@@ -30,13 +33,7 @@ export const GRID_SINGLE_SELECT_COL_DEF: Omit =
return '';
}
- let valueOptions: Array;
- if (typeof colDef.valueOptions === 'function') {
- valueOptions = colDef.valueOptions!({ id, row: id ? api.getRow(id) : null, field });
- } else {
- valueOptions = colDef.valueOptions!;
- }
-
+ const valueOptions = getValueOptions(colDef, { id, row: id ? api.getRow(id) : null });
if (value == null) {
return '';
}
@@ -56,12 +53,8 @@ export const GRID_SINGLE_SELECT_COL_DEF: Omit =
filterOperators: getGridSingleSelectOperators(),
// @ts-ignore
pastedValueParser: (value, params) => {
- const colDef = params.colDef;
- const colDefValueOptions = (colDef as GridSingleSelectColDef).valueOptions;
- const valueOptions =
- typeof colDefValueOptions === 'function'
- ? colDefValueOptions({ field: colDef.field })
- : colDefValueOptions || [];
+ const colDef = params.colDef as GridSingleSelectColDef;
+ const valueOptions = getValueOptions(colDef) || [];
const getOptionValue = (colDef as GridSingleSelectColDef).getOptionValue!;
const valueOption = valueOptions.find((option) => {
if (getOptionValue(option) === value) {
diff --git a/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx b/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx
index dd4fe8940fbb..0f0ac6412031 100644
--- a/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx
+++ b/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx
@@ -7,17 +7,16 @@ import { GridRenderEditCellParams } from '../../models/params/gridCellParams';
import { isEscapeKey } from '../../utils/keyboardUtils';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { GridEditModes } from '../../models/gridEditRowModel';
-import { GridSingleSelectColDef, ValueOptions } from '../../models/colDef/gridColDef';
import {
getValueFromValueOptions,
+ getValueOptions,
isSingleSelectColDef,
} from '../panel/filterPanel/filterPanelUtils';
import { useGridApiContext } from '../../hooks/utils/useGridApiContext';
export interface GridEditSingleSelectCellProps
extends GridRenderEditCellParams,
- Omit,
- Pick {
+ Omit {
/**
* Callback called when the value is changed by the user.
* @param {SelectChangeEvent} event The event source of the callback.
@@ -56,8 +55,6 @@ function GridEditSingleSelectCell(props: GridEditSingleSelectCellProps) {
error,
onValueChange,
initialOpen = rootProps.editMode === GridEditModes.Cell,
- getOptionLabel: getOptionLabelProp,
- getOptionValue: getOptionValueProp,
...other
} = props;
@@ -80,19 +77,13 @@ function GridEditSingleSelectCell(props: GridEditSingleSelectCellProps) {
return null;
}
- let valueOptions: Array | undefined;
- if (typeof colDef?.valueOptions === 'function') {
- valueOptions = colDef?.valueOptions({ id, row, field });
- } else {
- valueOptions = colDef?.valueOptions;
- }
-
+ const valueOptions = getValueOptions(colDef, { id, row });
if (!valueOptions) {
return null;
}
- const getOptionValue = getOptionValueProp || colDef.getOptionValue!;
- const getOptionLabel = getOptionLabelProp || colDef.getOptionLabel!;
+ const getOptionValue = colDef.getOptionValue!;
+ const getOptionLabel = colDef.getOptionLabel!;
const handleChange: SelectProps['onChange'] = async (event) => {
if (!isSingleSelectColDef(colDef) || !valueOptions) {
@@ -204,18 +195,6 @@ GridEditSingleSelectCell.propTypes = {
* The cell value formatted with the column valueFormatter.
*/
formattedValue: PropTypes.any,
- /**
- * Used to determine the label displayed for a given value option.
- * @param {ValueOptions} value The current value option.
- * @returns {string} The text to be displayed.
- */
- getOptionLabel: PropTypes.func,
- /**
- * Used to determine the value used for a value option.
- * @param {ValueOptions} value The current value option.
- * @returns {string} The value to be used.
- */
- getOptionValue: PropTypes.func,
/**
* If true, the cell is the active element.
*/
diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx
index ce527a1affac..691e3946aa1c 100644
--- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx
+++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx
@@ -16,7 +16,12 @@ import { useGridApiContext } from '../../../hooks/utils/useGridApiContext';
import { useGridRootProps } from '../../../hooks/utils/useGridRootProps';
import type { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { getDataGridUtilityClass } from '../../../constants/gridClasses';
-import { GridColDef, GridStateColDef } from '../../../models/colDef/gridColDef';
+import {
+ GridColDef,
+ GridSingleSelectColDef,
+ GridStateColDef,
+} from '../../../models/colDef/gridColDef';
+import { getValueFromValueOptions, getValueOptions } from './filterPanelUtils';
export interface FilterColumnsArgs {
field: GridColDef['field'];
@@ -233,16 +238,17 @@ const GridFilterForm = React.forwardRef(
const baseFormControlProps = rootProps.slotProps?.baseFormControl || {};
const baseSelectProps = rootProps.slotProps?.baseSelect || {};
- const isBaseSelectNative = baseSelectProps.native ?? true;
+ const isBaseSelectNative = baseSelectProps.native ?? false;
const baseInputLabelProps = rootProps.slotProps?.baseInputLabel || {};
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
const { InputComponentProps, ...valueInputPropsOther } = valueInputProps;
- const filteredColumns = React.useMemo(() => {
+ const { filteredColumns, selectedField } = React.useMemo(() => {
+ let itemField: string | undefined = item.field;
if (filterColumns === undefined || typeof filterColumns !== 'function') {
- return filterableColumns;
+ return { filteredColumns: filterableColumns, selectedField: itemField };
}
const filteredFields = filterColumns({
@@ -251,7 +257,16 @@ const GridFilterForm = React.forwardRef(
currentFilters: filterModel?.items || [],
});
- return filterableColumns.filter((column) => filteredFields.includes(column.field));
+ return {
+ filteredColumns: filterableColumns.filter((column) => {
+ const isFieldIncluded = filteredFields.includes(column.field);
+ if (column.field === item.field && !isFieldIncluded) {
+ itemField = undefined;
+ }
+ return isFieldIncluded;
+ }),
+ selectedField: itemField,
+ };
}, [filterColumns, filterModel?.items, filterableColumns, item.field]);
const sortedFilteredColumns = React.useMemo(() => {
@@ -297,16 +312,38 @@ const GridFilterForm = React.forwardRef(
column.filterOperators![0];
// Erase filter value if the input component or filtered column type is modified
- const eraseItemValue =
+ const eraseFilterValue =
!newOperator.InputComponent ||
newOperator.InputComponent !== currentOperator?.InputComponent ||
column.type !== currentColumn!.type;
+ let filterValue = eraseFilterValue ? undefined : item.value;
+
+ // Check filter value against the new valueOptions
+ if (column.type === 'singleSelect' && filterValue !== undefined) {
+ const colDef = column as GridSingleSelectColDef;
+ const valueOptions = getValueOptions(colDef);
+ if (Array.isArray(filterValue)) {
+ filterValue = filterValue.filter((val) => {
+ return (
+ // Only keep values that are in the new value options
+ getValueFromValueOptions(val, valueOptions, colDef?.getOptionValue!) !== undefined
+ );
+ });
+ } else if (
+ getValueFromValueOptions(item.value, valueOptions, colDef?.getOptionValue!) ===
+ undefined
+ ) {
+ // Reset the filter value if it is not in the new value options
+ filterValue = undefined;
+ }
+ }
+
applyFilterChanges({
...item,
field,
operator: newOperator.value,
- value: eraseItemValue ? undefined : item.value,
+ value: filterValue,
});
},
[apiRef, applyFilterChanges, item, currentColumn, currentOperator],
@@ -421,7 +458,7 @@ const GridFilterForm = React.forwardRef(
inputProps={{
'aria-label': apiRef.current.getLocaleText('filterPanelLogicOperator'),
}}
- value={multiFilterOperator}
+ value={multiFilterOperator ?? ''}
onChange={changeLogicOperator}
disabled={!!disableMultiFilterOperator || logicOperators.length === 1}
native={isBaseSelectNative}
@@ -462,7 +499,7 @@ const GridFilterForm = React.forwardRef(
labelId={columnSelectLabelId}
id={columnSelectId}
label={apiRef.current.getLocaleText('filterPanelColumns')}
- value={item.field || ''}
+ value={selectedField ?? ''}
onChange={changeColumn}
native={isBaseSelectNative}
{...rootProps.slotProps?.baseSelect}
@@ -541,6 +578,7 @@ const GridFilterForm = React.forwardRef(
item={item}
applyValue={applyFilterChanges}
focusElementRef={valueRef}
+ key={item.field}
{...currentOperator.InputComponentProps}
{...InputComponentProps}
/>
diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx
index 46f9585b38e2..32c79a69e6b7 100644
--- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx
+++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx
@@ -45,7 +45,7 @@ function GridFilterInputBoolean(props: GridFilterInputBooleanProps) {
const selectId = useId();
const baseSelectProps = rootProps.slotProps?.baseSelect || {};
- const isSelectNative = baseSelectProps.native ?? true;
+ const isSelectNative = baseSelectProps.native ?? false;
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputMultipleSingleSelect.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputMultipleSingleSelect.tsx
index c18299a319b3..94048d5b06a6 100644
--- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputMultipleSingleSelect.tsx
+++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputMultipleSingleSelect.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import Autocomplete, { AutocompleteProps, createFilterOptions } from '@mui/material/Autocomplete';
import { unstable_useId as useId } from '@mui/utils';
-import { isSingleSelectColDef } from './filterPanelUtils';
+import { getValueOptions, isSingleSelectColDef } from './filterPanelUtils';
import { useGridRootProps } from '../../../hooks/utils/useGridRootProps';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import type { GridSingleSelectColDef, ValueOptions } from '../../../models/colDef/gridColDef';
@@ -21,7 +21,6 @@ export interface GridFilterInputMultipleSingleSelectProps
| 'color'
| 'getOptionLabel'
>,
- Pick,
GridFilterInputValueProps {
type?: 'singleSelect';
}
@@ -40,8 +39,6 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl
helperText,
size,
variant = 'standard',
- getOptionLabel: getOptionLabelProp,
- getOptionValue: getOptionValueProp,
...other
} = props;
const TextFieldProps = {
@@ -63,8 +60,8 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl
}
}
- const getOptionValue = getOptionValueProp || resolvedColumn?.getOptionValue!;
- const getOptionLabel = getOptionLabelProp || resolvedColumn?.getOptionLabel!;
+ const getOptionValue = resolvedColumn?.getOptionValue!;
+ const getOptionLabel = resolvedColumn?.getOptionLabel!;
const isOptionEqualToValue = React.useCallback(
(option: ValueOptions, value: ValueOptions) => getOptionValue(option) === getOptionValue(value),
@@ -72,48 +69,17 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl
);
const resolvedValueOptions = React.useMemo(() => {
- if (!resolvedColumn?.valueOptions) {
- return [];
- }
-
- if (typeof resolvedColumn.valueOptions === 'function') {
- return resolvedColumn.valueOptions({ field: resolvedColumn.field });
- }
-
- return resolvedColumn.valueOptions;
+ return getValueOptions(resolvedColumn!) || [];
}, [resolvedColumn]);
- const resolvedFormattedValueOptions = React.useMemo(() => {
- return resolvedValueOptions?.map(getOptionValue);
- }, [resolvedValueOptions, getOptionValue]);
-
// The value is computed from the item.value and used directly
// If it was done by a useEffect/useState, the Autocomplete could receive incoherent value and options
const filteredValues = React.useMemo(() => {
if (!Array.isArray(item.value)) {
return [];
}
- if (resolvedValueOptions !== undefined) {
- const itemValueIndexes = item.value.map((element) => {
- // Gets the index matching between values and valueOptions
- return resolvedFormattedValueOptions?.findIndex(
- (formattedOption) => formattedOption === element,
- );
- });
-
- return itemValueIndexes
- .filter((index) => index >= 0)
- .map((index: number) => resolvedValueOptions[index]);
- }
return item.value;
- }, [item.value, resolvedValueOptions, resolvedFormattedValueOptions]);
-
- React.useEffect(() => {
- if (!Array.isArray(item.value) || filteredValues.length !== item.value.length) {
- // Updates the state if the filter value has been cleaned by the component
- applyValue({ ...item, value: filteredValues.map(getOptionValue) });
- }
- }, [item, filteredValues, applyValue, getOptionValue]);
+ }, [item.value]);
const handleChange = React.useCallback<
NonNullable['onChange']>
@@ -177,18 +143,6 @@ GridFilterInputMultipleSingleSelect.propTypes = {
PropTypes.func,
PropTypes.object,
]),
- /**
- * Used to determine the label displayed for a given value option.
- * @param {ValueOptions} value The current value option.
- * @returns {string} The text to be displayed.
- */
- getOptionLabel: PropTypes.func,
- /**
- * Used to determine the value used for a value option.
- * @param {ValueOptions} value The current value option.
- * @returns {string} The value to be used.
- */
- getOptionValue: PropTypes.func,
item: PropTypes.shape({
field: PropTypes.string.isRequired,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx
index 73d8da458e7b..43994aa5409f 100644
--- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx
+++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx
@@ -7,11 +7,15 @@ import { styled } from '@mui/material/styles';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import { GridSingleSelectColDef } from '../../../models/colDef/gridColDef';
import { useGridRootProps } from '../../../hooks/utils/useGridRootProps';
-import { getValueFromValueOptions, isSingleSelectColDef } from './filterPanelUtils';
+import {
+ getValueFromValueOptions,
+ getValueOptions,
+ isSingleSelectColDef,
+} from './filterPanelUtils';
import type { GridSlotsComponentsProps } from '../../../models/gridSlotsComponentsProps';
const renderSingleSelectOptions = ({
- column: { valueOptions, field },
+ column,
OptionComponent,
getOptionLabel,
getOptionValue,
@@ -25,14 +29,14 @@ const renderSingleSelectOptions = ({
isSelectNative: boolean;
baseSelectOptionProps: GridSlotsComponentsProps['baseSelectOption'];
}) => {
- const iterableColumnValues =
- typeof valueOptions === 'function'
- ? ['', ...valueOptions({ field })]
- : ['', ...(valueOptions || [])];
+ const iterableColumnValues = ['', ...(getValueOptions(column) || [])];
return iterableColumnValues.map((option) => {
const value = getOptionValue(option);
- const label = getOptionLabel(option);
+ let label = getOptionLabel(option);
+ if (label === '') {
+ label = ' '; // To force the height of the empty option
+ }
return (
@@ -52,8 +56,7 @@ const SingleSelectOperatorContainer = styled('div')({
});
export type GridFilterInputSingleSelectProps = GridFilterInputValueProps &
- TextFieldProps &
- Pick & {
+ TextFieldProps & {
clearButton?: React.ReactNode | null;
/**
* It is `true` if the filter either has a value or an operator with no value
@@ -70,8 +73,6 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
type,
apiRef,
focusElementRef,
- getOptionLabel: getOptionLabelProp,
- getOptionValue: getOptionValueProp,
placeholder,
tabIndex,
label: labelProp,
@@ -80,12 +81,12 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
InputLabelProps,
...others
} = props;
- const [filterValueState, setFilterValueState] = React.useState(item.value ?? '');
+ const filterValue = item.value ?? '';
const id = useId();
const labelId = useId();
const rootProps = useGridRootProps();
- const isSelectNative = rootProps.slotProps?.baseSelect?.native ?? true;
+ const isSelectNative = rootProps.slotProps?.baseSelect?.native ?? false;
let resolvedColumn: GridSingleSelectColDef | null = null;
if (item.field) {
@@ -95,16 +96,11 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
}
}
- const getOptionValue = getOptionValueProp || resolvedColumn?.getOptionValue!;
- const getOptionLabel = getOptionLabelProp || resolvedColumn?.getOptionLabel!;
+ const getOptionValue = resolvedColumn?.getOptionValue!;
+ const getOptionLabel = resolvedColumn?.getOptionLabel!;
const currentValueOptions = React.useMemo(() => {
- if (!resolvedColumn) {
- return undefined;
- }
- return typeof resolvedColumn.valueOptions === 'function'
- ? resolvedColumn.valueOptions({ field: resolvedColumn.field })
- : resolvedColumn.valueOptions;
+ return getValueOptions(resolvedColumn!);
}, [resolvedColumn]);
const onFilterChange = React.useCallback(
@@ -113,36 +109,11 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
// NativeSelect casts the value to a string.
value = getValueFromValueOptions(value, currentValueOptions, getOptionValue);
-
- setFilterValueState(String(value));
applyValue({ ...item, value });
},
[currentValueOptions, getOptionValue, applyValue, item],
);
- React.useEffect(() => {
- let itemValue;
-
- if (currentValueOptions !== undefined) {
- // sanitize if valueOptions are provided
- itemValue = getValueFromValueOptions(item.value, currentValueOptions, getOptionValue);
- if (itemValue !== item.value) {
- applyValue({ ...item, value: itemValue });
- return;
- }
- } else {
- itemValue = item.value;
- }
-
- itemValue = itemValue ?? '';
-
- setFilterValueState(String(itemValue));
- }, [item, currentValueOptions, applyValue, getOptionValue]);
-
- if (!isSingleSelectColDef(resolvedColumn)) {
- return null;
- }
-
if (!isSingleSelectColDef(resolvedColumn)) {
return null;
}
@@ -151,7 +122,7 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
return (
-
+
,
+) {
+ if (!column) {
+ return undefined;
+ }
+ return typeof column.valueOptions === 'function'
+ ? column.valueOptions({ field: column.field, ...additionalParams })
+ : column.valueOptions;
+}
+
export function getValueFromValueOptions(
value: string,
valueOptions: any[] | undefined,
@@ -22,8 +31,3 @@ export function getValueFromValueOptions(
});
return getOptionValue(result);
}
-
-export const getLabelFromValueOption = (valueOption: ValueOptions) => {
- const label = typeof valueOption === 'object' ? valueOption.label : valueOption;
- return label != null ? String(label) : '';
-};
diff --git a/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx
index b1c09140fc95..20e3b581f83e 100644
--- a/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx
+++ b/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx
@@ -10,16 +10,16 @@ import {
GridPreferencePanelsValue,
} from '@mui/x-data-grid';
import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils';
-import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn';
+import { getColumnHeaderCell, getColumnValues, getSelectByName } from 'test/utils/helperFn';
function setColumnValue(columnValue: string) {
- fireEvent.change(screen.getByRole('combobox', { name: 'Columns' }), {
+ fireEvent.change(getSelectByName('Columns'), {
target: { value: columnValue },
});
}
function setOperatorValue(operator: string) {
- fireEvent.change(screen.getByRole('combobox', { name: 'Operator' }), {
+ fireEvent.change(getSelectByName('Operator'), {
target: { value: operator },
});
}
@@ -175,17 +175,13 @@ describe(' - Filter panel', () => {
/>,
);
expect(screen.getByRole('textbox', { name: 'Value' }).value).to.equal('Puma');
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'equals',
- );
+ expect(getSelectByName('Operator').value).to.equal('equals');
expect(getColumnValues(0)).to.deep.equal(['Puma']);
setColumnValue('slogan');
expect(getColumnValues(0)).to.deep.equal([]);
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'equals',
- );
+ expect(getSelectByName('Operator').value).to.equal('equals');
expect(screen.getByRole('textbox', { name: 'Value' }).value).to.equal('Puma');
});
@@ -212,17 +208,13 @@ describe(' - Filter panel', () => {
/>,
);
expect(screen.getByRole('textbox', { name: 'Value' }).value).to.equal('Pu');
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'contains',
- );
+ expect(getSelectByName('Operator').value).to.equal('contains');
expect(getColumnValues(0)).to.deep.equal(['Puma']);
setColumnValue('slogan');
expect(getColumnValues(0)).to.deep.equal([]);
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'from',
- );
+ expect(getSelectByName('Operator').value).to.equal('from');
expect(screen.getByTestId('customInput').value).to.equal('');
});
@@ -252,9 +244,7 @@ describe(' - Filter panel', () => {
/>,
);
expect(screen.getByRole('textbox', { name: 'Value' }).value).to.equal('Pu');
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'contains',
- );
+ expect(getSelectByName('Operator').value).to.equal('contains');
expect(getColumnValues(0)).to.deep.equal(['Puma']);
expect(onFilterModelChange.callCount).to.equal(0);
@@ -264,9 +254,7 @@ describe(' - Filter panel', () => {
expect(onFilterModelChange.callCount).to.equal(1);
expect(onFilterModelChange.lastCall.args[0].items[0].value).to.equal(undefined);
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'isEmpty',
- );
+ expect(getSelectByName('Operator').value).to.equal('isEmpty');
});
it('should reset filter value if not available in the new valueOptions', () => {
@@ -508,12 +496,8 @@ describe(' - Filter panel', () => {
fireEvent.click(screen.getByRole('menuitem', { name: 'Filter' }));
// check that the filter is still in the model
- expect(screen.getByRole('combobox', { name: 'Columns' }).value).to.equal(
- 'brand',
- );
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'isEmpty',
- );
+ expect(getSelectByName('Columns').value).to.equal('brand');
+ expect(getSelectByName('Operator').value).to.equal('isEmpty');
});
// See https://github.com/mui/mui-x/issues/7901#issuecomment-1427058922
@@ -537,11 +521,7 @@ describe(' - Filter panel', () => {
fireEvent.click(screen.getByRole('menuitem', { name: 'Filter' }));
// check that the filter is changed to default one (`is`)
- expect(screen.getByRole('combobox', { name: 'Columns' }).value).to.equal(
- 'country',
- );
- expect(screen.getByRole('combobox', { name: 'Operator' }).value).to.equal(
- 'is',
- );
+ expect(getSelectByName('Columns').value).to.equal('country');
+ expect(getSelectByName('Operator').value).to.equal('is');
});
});
diff --git a/test/utils/helperFn.ts b/test/utils/helperFn.ts
index 0c72d6fcc421..ca0258f2ee0f 100644
--- a/test/utils/helperFn.ts
+++ b/test/utils/helperFn.ts
@@ -1,5 +1,5 @@
import { spy } from 'sinon';
-import { act } from '@mui-internal/test-utils';
+import { act, screen } from '@mui-internal/test-utils';
import { unwrapPrivateAPI } from '@mui/x-data-grid/internals';
import type { GridApiCommon } from '@mui/x-data-grid/models/api/gridApiCommon';
@@ -137,3 +137,22 @@ export function getRow(rowIndex: number): HTMLElement {
}
return row;
}
+
+/**
+ * Returns the hidden `input` element of the Material UI Select component
+ */
+export const getSelectInput = (combobox: Element) => {
+ if (!combobox) {
+ return null;
+ }
+ const comboboxParent = combobox.parentElement;
+ if (!comboboxParent) {
+ return null;
+ }
+ const input = comboboxParent.querySelector('input');
+ return input;
+};
+
+export function getSelectByName(name: string) {
+ return getSelectInput(screen.getByRole('combobox', { name }))!;
+}