diff --git a/packages/grid/x-data-grid-generator/src/services/gridColDefGenerator.ts b/packages/grid/x-data-grid-generator/src/services/gridColDefGenerator.ts index f93a835816cbd..b082329b6ae7a 100644 --- a/packages/grid/x-data-grid-generator/src/services/gridColDefGenerator.ts +++ b/packages/grid/x-data-grid-generator/src/services/gridColDefGenerator.ts @@ -1,4 +1,4 @@ -import { GridBaseColDef } from '@mui/x-data-grid-pro/internals'; +import { GridColDef } from '@mui/x-data-grid-pro'; export interface GridDataGeneratorContext { /** @@ -10,7 +10,7 @@ export interface GridDataGeneratorContext { values?: Record; } -export interface GridColDefGenerator extends GridBaseColDef { +export type GridColDefGenerator = GridColDef & { generateData?: (row: any, context: GridDataGeneratorContext) => any; /** @@ -23,4 +23,4 @@ export interface GridColDefGenerator extends GridBaseColDef { * If `true`, the column will be marked as hidden in the `columnVisibilityModel`. */ hide?: boolean; -} +}; diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/export/serializer/excelSerializer.ts b/packages/grid/x-data-grid-premium/src/hooks/features/export/serializer/excelSerializer.ts index 02d136ec3485a..ba97b71cac07b 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/export/serializer/excelSerializer.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/export/serializer/excelSerializer.ts @@ -8,7 +8,13 @@ import { GRID_DATE_COL_DEF, GRID_DATETIME_COL_DEF, } from '@mui/x-data-grid-pro'; -import { buildWarning, GridStateColDef, isObject } from '@mui/x-data-grid/internals'; +import { + buildWarning, + GridStateColDef, + GridSingleSelectColDef, + isObject, + isSingleSelectColDef, +} from '@mui/x-data-grid/internals'; import { GridExceljsProcessInput, ColumnsStylesInterface } from '../gridExcelExportInterface'; import { GridPrivateApiPremium } from '../../../../models/gridApiPremium'; @@ -23,7 +29,7 @@ const warnInvalidFormattedValue = buildWarning([ ]); const getFormattedValueOptions = ( - colDef: GridColDef, + colDef: GridSingleSelectColDef, valueOptions: ValueOptions[], api: GridApi, ) => { @@ -84,16 +90,17 @@ const serializeRow = ( switch (cellParams.colDef.type) { case 'singleSelect': { - if (typeof cellParams.colDef.valueOptions === 'function') { + const castColumn = cellParams.colDef as GridSingleSelectColDef; + if (typeof castColumn.valueOptions === 'function') { // If value option depends on the row, set specific options to the cell // This dataValidation is buggy with LibreOffice and does not allow to have coma - const valueOptions = cellParams.colDef.valueOptions({ id, row, field: cellParams.field }); - const formattedValueOptions = getFormattedValueOptions( - cellParams.colDef, - valueOptions, - api, - ); - dataValidation[column.field] = { + const valueOptions = castColumn.valueOptions({ + id, + row, + field: cellParams.field, + }); + const formattedValueOptions = getFormattedValueOptions(castColumn, valueOptions, api); + dataValidation[castColumn.field] = { type: 'list', allowBlank: true, formulae: [ @@ -104,23 +111,23 @@ const serializeRow = ( }; } else { // If value option is defined for the column, refer to another sheet - dataValidation[column.field] = { + dataValidation[castColumn.field] = { type: 'list', allowBlank: true, - formulae: [defaultValueOptionsFormulae[column.field]], + formulae: [defaultValueOptionsFormulae[castColumn.field]], }; } - const formattedValue = api.getCellParams(id, column.field).formattedValue; + const formattedValue = api.getCellParams(id, castColumn.field).formattedValue; if (process.env.NODE_ENV !== 'production') { if (String(cellParams.formattedValue) === '[object Object]') { warnInvalidFormattedValue(); } } if (isObject<{ label: any }>(formattedValue)) { - row[column.field] = formattedValue?.label; + row[castColumn.field] = formattedValue?.label; } else { - row[column.field] = formattedValue as any; + row[castColumn.field] = formattedValue as any; } break; } @@ -296,10 +303,10 @@ export async function buildExcel( const columnsWithArrayValueOptions = columns.filter( (column) => - column.type === 'singleSelect' && + isSingleSelectColDef(column) && column.valueOptions && typeof column.valueOptions !== 'function', - ); + ) as GridSingleSelectColDef[]; const defaultValueOptionsFormulae: { [field: string]: string } = {}; if (columnsWithArrayValueOptions.length) { diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx index 5ac63eea820b9..40f3457d272fb 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx @@ -7,7 +7,7 @@ import { GridGroupingColDefOverride, GridGroupNode, } from '@mui/x-data-grid-pro'; -import { GridColumnRawLookup } from '@mui/x-data-grid-pro/internals'; +import { GridColumnRawLookup, isSingleSelectColDef } from '@mui/x-data-grid-pro/internals'; import { GridApiPremium } from '../../../models/gridApiPremium'; import { GridGroupingColumnFooterCell } from '../../../components/GridGroupingColumnFooterCell'; import { GridGroupingCriteriaCell } from '../../../components/GridGroupingCriteriaCell'; @@ -66,7 +66,7 @@ const getLeafProperties = (leafColDef: GridColDef): Partial => ({ headerName: leafColDef.headerName ?? leafColDef.field, sortable: leafColDef.sortable, filterable: leafColDef.filterable, - valueOptions: leafColDef.valueOptions, + valueOptions: isSingleSelectColDef(leafColDef) ? leafColDef.valueOptions : undefined, filterOperators: leafColDef.filterOperators?.map((operator) => ({ ...operator, getApplyFilterFn: (filterItem, column) => { @@ -94,7 +94,7 @@ const getGroupingCriteriaProperties = (groupedByColDef: GridColDef, applyHeaderN const properties: Partial = { sortable: groupedByColDef.sortable, filterable: groupedByColDef.filterable, - valueOptions: groupedByColDef.valueOptions, + valueOptions: isSingleSelectColDef(groupedByColDef) ? groupedByColDef.valueOptions : undefined, sortComparator: (v1, v2, cellParams1, cellParams2) => { // We only want to sort the groups of the current grouping criteria if ( diff --git a/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx b/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx index 7b1863f8c7174..8ed353a159f51 100644 --- a/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx +++ b/packages/grid/x-data-grid/src/colDef/gridSingleSelectColDef.tsx @@ -1,20 +1,34 @@ import { GRID_STRING_COL_DEF } from './gridStringColDef'; -import { GridColTypeDef, ValueOptions } from '../models/colDef/gridColDef'; +import { GridSingleSelectColDef, ValueOptions } from '../models/colDef/gridColDef'; import { renderEditSingleSelectCell } from '../components/cell/GridEditSingleSelectCell'; import { getGridSingleSelectOperators } from './gridSingleSelectOperators'; -import { getLabelFromValueOption } from '../components/panel/filterPanel/filterPanelUtils'; +import { isSingleSelectColDef } from '../components/panel/filterPanel/filterPanelUtils'; const isArrayOfObjects = (options: any): options is Array<{ value: any; label: string }> => { return typeof options[0] === 'object'; }; -export const GRID_SINGLE_SELECT_COL_DEF: GridColTypeDef = { +const defaultGetOptionValue = (value: ValueOptions) => { + return typeof value === 'object' ? value.value : value; +}; + +const defaultGetOptionLabel = (value: ValueOptions) => { + return typeof value === 'object' ? value.label : String(value); +}; + +export const GRID_SINGLE_SELECT_COL_DEF: Omit = { ...GRID_STRING_COL_DEF, type: 'singleSelect', + getOptionLabel: defaultGetOptionLabel, + getOptionValue: defaultGetOptionValue, valueFormatter(params) { const { id, field, value, api } = params; const colDef = params.api.getColumn(field); + if (!isSingleSelectColDef(colDef)) { + return ''; + } + let valueOptions: Array; if (typeof colDef.valueOptions === 'function') { valueOptions = colDef.valueOptions!({ id, row: id ? api.getRow(id) : null, field }); @@ -31,11 +45,11 @@ export const GRID_SINGLE_SELECT_COL_DEF: GridColTypeDef = { } if (!isArrayOfObjects(valueOptions)) { - return getLabelFromValueOption(value); + return colDef.getOptionLabel!(value); } const valueOption = valueOptions.find((option) => option.value === value); - return valueOption ? getLabelFromValueOption(valueOption) : ''; + return valueOption ? colDef.getOptionLabel!(valueOption) : ''; }, renderEditCell: renderEditSingleSelectCell, filterOperators: getGridSingleSelectOperators(), 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 eb17895500e8a..cad814ce6393c 100644 --- a/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx +++ b/packages/grid/x-data-grid/src/components/cell/GridEditSingleSelectCell.tsx @@ -7,16 +7,17 @@ import { GridRenderEditCellParams } from '../../models/params/gridCellParams'; import { isEscapeKey } from '../../utils/keyboardUtils'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridEditModes } from '../../models/gridEditRowModel'; -import { ValueOptions } from '../../models/colDef/gridColDef'; +import { GridSingleSelectColDef, ValueOptions } from '../../models/colDef/gridColDef'; import { - getLabelFromValueOption, getValueFromValueOptions, + isSingleSelectColDef, } from '../panel/filterPanel/filterPanelUtils'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; export interface GridEditSingleSelectCellProps extends GridRenderEditCellParams, - Omit { + Omit, + Pick { /** * Callback called when the value is changed by the user. * @param {SelectChangeEvent} event The event source of the callback. @@ -28,12 +29,6 @@ export interface GridEditSingleSelectCellProps * If true, the select opens by default. */ initialOpen?: boolean; - /** - * Used to determine the text displayed for a given value option. - * @param {ValueOptions} value The current value option. - * @returns {string} The text to be displayed. - */ - getOptionLabel?: (value: ValueOptions) => string; } function isKeyboardEvent(event: any): event is React.KeyboardEvent { @@ -61,7 +56,8 @@ function GridEditSingleSelectCell(props: GridEditSingleSelectCellProps) { error, onValueChange, initialOpen = rootProps.editMode === GridEditModes.Cell, - getOptionLabel = getLabelFromValueOption, + getOptionLabel: getOptionLabelProp, + getOptionValue: getOptionValueProp, ...other } = props; @@ -73,18 +69,34 @@ function GridEditSingleSelectCell(props: GridEditSingleSelectCellProps) { const baseSelectProps = rootProps.componentsProps?.baseSelect || {}; const isSelectNative = baseSelectProps.native ?? false; - let valueOptions: Array; - if (typeof colDef.valueOptions === 'function') { - valueOptions = colDef.valueOptions!({ id, row, field }); + let resolvedColumn: GridSingleSelectColDef | null = null; + if (isSingleSelectColDef(colDef)) { + resolvedColumn = colDef; + } + + const getOptionValue = getOptionValueProp || resolvedColumn?.getOptionValue!; + const getOptionLabel = getOptionLabelProp || resolvedColumn?.getOptionLabel!; + + let valueOptions: Array | null = null; + if (typeof resolvedColumn?.valueOptions === 'function') { + valueOptions = resolvedColumn?.valueOptions!({ id, row, field }); } else { - valueOptions = colDef.valueOptions!; + valueOptions = resolvedColumn?.valueOptions!; } const handleChange: SelectProps['onChange'] = async (event) => { + if (!isSingleSelectColDef(colDef) || !valueOptions) { + return; + } + setOpen(false); const target = event.target as HTMLInputElement; // NativeSelect casts the value to a string. - const formattedTargetValue = getValueFromValueOptions(target.value, valueOptions); + const formattedTargetValue = getValueFromValueOptions( + target.value, + valueOptions, + getOptionValue, + ); if (onValueChange) { await onValueChange(event, formattedTargetValue); @@ -118,6 +130,10 @@ function GridEditSingleSelectCell(props: GridEditSingleSelectCellProps) { const OptionComponent = isSelectNative ? 'option' : MenuItem; + if (!valueOptions || !resolvedColumn) { + return null; + } + return ( {valueOptions.map((valueOption) => { - const value = typeof valueOption === 'object' ? valueOption.value : valueOption; + const value = getOptionValue(valueOption); return ( 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 418e81812b6a6..c8af528593c72 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 @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import Autocomplete, { AutocompleteProps, createFilterOptions } from '@mui/material/Autocomplete'; import Chip from '@mui/material/Chip'; import { unstable_useId as useId } from '@mui/utils'; -import { getLabelFromValueOption, getValueFromOption } from './filterPanelUtils'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; import { GridFilterInputValueProps } from './GridFilterInputValueProps'; -import type { ValueOptions } from '../../../models/colDef/gridColDef'; +import type { GridSingleSelectColDef, ValueOptions } from '../../../models/colDef/gridColDef'; +import { isSingleSelectColDef } from './filterPanelUtils'; export interface GridFilterInputMultipleSingleSelectProps extends Omit< @@ -22,24 +22,11 @@ export interface GridFilterInputMultipleSingleSelectProps | 'color' | 'getOptionLabel' >, + Pick, GridFilterInputValueProps { type?: 'singleSelect'; - /** - * Used to determine the text displayed for a given value option. - * @param {ValueOptions} value The current value option. - * @returns {string} The text to be displayed. - */ - getOptionLabel?: (value: ValueOptions) => string; } -const isOptionEqualToValue: AutocompleteProps< - ValueOptions, - true, - false, - true ->['isOptionEqualToValue'] = (option, value) => - getValueFromOption(option) === getValueFromOption(value); - const filter = createFilterOptions(); function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingleSelectProps) { @@ -54,7 +41,8 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl helperText, size, variant = 'standard', - getOptionLabel = getLabelFromValueOption, + getOptionLabel: getOptionLabelProp, + getOptionValue: getOptionValueProp, ...other } = props; const TextFieldProps = { @@ -68,7 +56,22 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl const id = useId(); const rootProps = useGridRootProps(); - const resolvedColumn = item.field ? apiRef.current.getColumn(item.field) : null; + let resolvedColumn: GridSingleSelectColDef | null = null; + if (item.field) { + const column = apiRef.current.getColumn(item.field); + if (isSingleSelectColDef(column)) { + resolvedColumn = column; + } + } + + const getOptionValue = getOptionValueProp || resolvedColumn?.getOptionValue!; + const getOptionLabel = getOptionLabelProp || resolvedColumn?.getOptionLabel!; + + const isOptionEqualToValue = React.useCallback( + (option: ValueOptions, value: ValueOptions) => getOptionValue(option) === getOptionValue(value), + [getOptionValue], + ); + const resolvedValueOptions = React.useMemo(() => { if (!resolvedColumn?.valueOptions) { return []; @@ -80,26 +83,23 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl return resolvedColumn.valueOptions; }, [resolvedColumn]); + const resolvedFormattedValueOptions = React.useMemo(() => { - return resolvedValueOptions?.map(getValueFromOption); - }, [resolvedValueOptions]); + 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 filterValues = React.useMemo(() => { + const filteredValues = React.useMemo(() => { if (!Array.isArray(item.value)) { return []; } if (resolvedValueOptions !== undefined) { const itemValueIndexes = item.value.map((element) => { - // get the index matching between values and valueOptions - const formattedElement = getValueFromOption(element); - const index = - resolvedFormattedValueOptions?.findIndex( - (formatedOption) => formatedOption === formattedElement, - ) || 0; - - return index; + // Gets the index matching between values and valueOptions + return resolvedFormattedValueOptions?.findIndex( + (formatedOption) => formatedOption === element, + ); }); return itemValueIndexes @@ -110,19 +110,19 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl }, [item.value, resolvedValueOptions, resolvedFormattedValueOptions]); React.useEffect(() => { - if (!Array.isArray(item.value) || filterValues.length !== item.value.length) { - // update the state if the filter value has been cleaned by the component - applyValue({ ...item, value: filterValues.map(getValueFromOption) }); + 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, filterValues, applyValue]); + }, [item, filteredValues, applyValue, getOptionValue]); const handleChange = React.useCallback< NonNullable['onChange']> >( (event, value) => { - applyValue({ ...item, value: [...value.map(getValueFromOption)] }); + applyValue({ ...item, value: value.map(getOptionValue) }); }, - [applyValue, item], + [applyValue, item, getOptionValue], ); return ( @@ -132,7 +132,7 @@ function GridFilterInputMultipleSingleSelect(props: GridFilterInputMultipleSingl isOptionEqualToValue={isOptionEqualToValue} filterOptions={filter} id={id} - value={filterValues} + value={filteredValues} onChange={handleChange} getOptionLabel={getOptionLabel} renderTags={(value, getTagProps) => 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 26c95e3d794a1..994514f005ec0 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 @@ -4,14 +4,15 @@ import { TextFieldProps } from '@mui/material/TextField'; import { unstable_useId as useId } from '@mui/utils'; import MenuItem from '@mui/material/MenuItem'; import { GridFilterInputValueProps } from './GridFilterInputValueProps'; -import { GridColDef, ValueOptions } from '../../../models/colDef/gridColDef'; +import { GridSingleSelectColDef, ValueOptions } from '../../../models/colDef/gridColDef'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; -import { getLabelFromValueOption, getValueFromValueOptions } from './filterPanelUtils'; +import { getValueFromValueOptions, isSingleSelectColDef } from './filterPanelUtils'; const renderSingleSelectOptions = ( - { valueOptions, field }: GridColDef, + { valueOptions, field }: GridSingleSelectColDef, OptionComponent: React.ElementType, - getOptionLabel: (value: ValueOptions) => React.ReactNode, + getOptionLabel: (value: ValueOptions) => string, + getOptionValue: (value: ValueOptions) => any, ) => { const iterableColumnValues = typeof valueOptions === 'function' @@ -19,9 +20,7 @@ const renderSingleSelectOptions = ( : ['', ...(valueOptions || [])]; return iterableColumnValues.map((option) => { - const isOptionTypeObject = typeof option === 'object'; - - const value = isOptionTypeObject ? option.value : option; + const value = getOptionValue(option); const label = getOptionLabel(option); return ( @@ -33,14 +32,9 @@ const renderSingleSelectOptions = ( }; export type GridFilterInputSingleSelectProps = GridFilterInputValueProps & - TextFieldProps & { + TextFieldProps & + Pick & { type?: 'singleSelect'; - /** - * Used to determine the text displayed for a given value option. - * @param {ValueOptions} value The current value option. - * @returns {string} The text to be displayed. - */ - getOptionLabel?: (value: ValueOptions) => string; }; function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) { @@ -50,7 +44,8 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) { type, apiRef, focusElementRef, - getOptionLabel = getLabelFromValueOption, + getOptionLabel: getOptionLabelProp, + getOptionValue: getOptionValueProp, ...others } = props; const [filterValueState, setFilterValueState] = React.useState(item.value ?? ''); @@ -60,28 +55,37 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) { const baseSelectProps = rootProps.componentsProps?.baseSelect || {}; const isSelectNative = baseSelectProps.native ?? true; - const currentColumn = item.field ? apiRef.current.getColumn(item.field) : null; + let resolvedColumn: GridSingleSelectColDef | null = null; + if (item.field) { + const column = apiRef.current.getColumn(item.field); + if (isSingleSelectColDef(column)) { + resolvedColumn = column; + } + } + + const getOptionValue = getOptionValueProp || resolvedColumn?.getOptionValue!; + const getOptionLabel = getOptionLabelProp || resolvedColumn?.getOptionLabel!; const currentValueOptions = React.useMemo(() => { - if (currentColumn === null) { + if (!resolvedColumn) { return undefined; } - return typeof currentColumn.valueOptions === 'function' - ? currentColumn.valueOptions({ field: currentColumn.field }) - : currentColumn.valueOptions; - }, [currentColumn]); + return typeof resolvedColumn.valueOptions === 'function' + ? resolvedColumn.valueOptions({ field: resolvedColumn.field }) + : resolvedColumn.valueOptions; + }, [resolvedColumn]); const onFilterChange = React.useCallback( (event: React.ChangeEvent) => { let value = event.target.value; // NativeSelect casts the value to a string. - value = getValueFromValueOptions(value, currentValueOptions); + value = getValueFromValueOptions(value, currentValueOptions, getOptionValue); setFilterValueState(String(value)); applyValue({ ...item, value }); }, - [applyValue, item, currentValueOptions], + [currentValueOptions, getOptionValue, applyValue, item], ); React.useEffect(() => { @@ -89,7 +93,7 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) { if (currentValueOptions !== undefined) { // sanitize if valueOptions are provided - itemValue = getValueFromValueOptions(item.value, currentValueOptions); + itemValue = getValueFromValueOptions(item.value, currentValueOptions, getOptionValue); if (itemValue !== item.value) { applyValue({ ...item, value: itemValue }); return; @@ -101,7 +105,11 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) { itemValue = itemValue ?? ''; setFilterValueState(String(itemValue)); - }, [item, currentValueOptions, applyValue]); + }, [item, currentValueOptions, applyValue, getOptionValue]); + + if (!isSingleSelectColDef(resolvedColumn)) { + return null; + } return ( {renderSingleSelectOptions( - apiRef.current.getColumn(item.field), + resolvedColumn, isSelectNative ? 'option' : MenuItem, getOptionLabel, + getOptionValue, )} ); diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/filterPanelUtils.ts b/packages/grid/x-data-grid/src/components/panel/filterPanel/filterPanelUtils.ts index 2d9d098ebc01e..639caf97f50d1 100644 --- a/packages/grid/x-data-grid/src/components/panel/filterPanel/filterPanelUtils.ts +++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/filterPanelUtils.ts @@ -1,21 +1,26 @@ -import type { ValueOptions } from '../../../models/colDef/gridColDef'; +import type { + GridColDef, + GridSingleSelectColDef, + ValueOptions, +} from '../../../models/colDef/gridColDef'; -export function getValueFromOption(option: any | undefined) { - if (typeof option === 'object' && option !== null) { - return option.value; - } - return option; +export function isSingleSelectColDef(colDef: GridColDef | null): colDef is GridSingleSelectColDef { + return colDef?.type === 'singleSelect'; } -export function getValueFromValueOptions(value: string, valueOptions?: any[]) { +export function getValueFromValueOptions( + value: string, + valueOptions: any[] | undefined, + getOptionValue: (value: ValueOptions) => any, +) { if (valueOptions === undefined) { return undefined; } const result = valueOptions.find((option) => { - const optionValue = getValueFromOption(option); + const optionValue = getOptionValue(option); return String(optionValue) === String(value); }); - return getValueFromOption(result); + return getOptionValue(result); } export const getLabelFromValueOption = (valueOption: ValueOptions) => { diff --git a/packages/grid/x-data-grid/src/internals/index.ts b/packages/grid/x-data-grid/src/internals/index.ts index c96dcd343e100..7649c7b6eb452 100644 --- a/packages/grid/x-data-grid/src/internals/index.ts +++ b/packages/grid/x-data-grid/src/internals/index.ts @@ -1,4 +1,8 @@ -export type { GridBaseColDef, GridStateColDef } from '../models/colDef/gridColDef'; +export type { + GridBaseColDef, + GridStateColDef, + GridSingleSelectColDef, +} from '../models/colDef/gridColDef'; export { GridVirtualScroller } from '../components/virtualization/GridVirtualScroller'; export { GridVirtualScrollerContent } from '../components/virtualization/GridVirtualScrollerContent'; export { GridVirtualScrollerRenderZone } from '../components/virtualization/GridVirtualScrollerRenderZone'; @@ -37,6 +41,7 @@ export { useGridCsvExport } from '../hooks/features/export/useGridCsvExport'; export { useGridPrintExport } from '../hooks/features/export/useGridPrintExport'; export { useGridFilter, filterStateInitializer } from '../hooks/features/filter/useGridFilter'; export { passFilterLogic } from '../hooks/features/filter/gridFilterUtils'; +export { isSingleSelectColDef } from '../components/panel/filterPanel/filterPanelUtils'; export type { GridAggregatedFilterItemApplier } from '../hooks/features/filter/gridFilterState'; export { useGridFocus, focusStateInitializer } from '../hooks/features/focus/useGridFocus'; export { useGridKeyboardNavigation } from '../hooks/features/keyboardNavigation/useGridKeyboardNavigation'; diff --git a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts index 410c668194e0b..49712984db2e4 100644 --- a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts +++ b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts @@ -114,10 +114,6 @@ export interface GridBaseColDef | ((params: GridValueOptionsParams) => Array); /** * Allows to align the column values in cells. */ @@ -257,12 +253,38 @@ export interface GridActionsColDef) => React.ReactElement[]; } +export interface GridSingleSelectColDef + extends GridBaseColDef { + /** + * Type allows to merge this object with a default definition [[GridColDef]]. + * @default 'singleSelect' + */ + type: 'singleSelect'; + /** + * To be used in combination with `type: 'singleSelect'`. This is an array (or a function returning an array) of the possible cell values and labels. + */ + valueOptions?: Array | ((params: GridValueOptionsParams) => Array); + /** + * 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?: (value: ValueOptions) => string; + /** + * 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?: (value: ValueOptions) => any; +} + /** * Column Definition interface. */ export type GridColDef = | GridBaseColDef - | GridActionsColDef; + | GridActionsColDef + | GridSingleSelectColDef; export type GridColTypeDef = Omit, 'field'> & { extendType?: GridNativeColTypes;