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

[DataGrid] Enable using non-native Select in filter panel #4361

Merged
merged 6 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { GridEditModes } from '../../models/gridEditRowModel';
import { GridEvents } from '../../models/events/gridEvents';
import { GridColDef, ValueOptions } from '../../models/colDef/gridColDef';
import { getValueFromValueOptions } from '../panel/filterPanel/filterPanelUtils';

const renderSingleSelectOptions = (option: ValueOptions) =>
typeof option === 'object' ? (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
) : (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
const renderSingleSelectOptions = (option: ValueOptions, OptionComponent: React.ElementType) => {
const isOptionTypeObject = typeof option === 'object';

const key = isOptionTypeObject ? option.value : option;
const value = isOptionTypeObject ? option.value : option;
const content = isOptionTypeObject ? option.label : option;

return (
<OptionComponent key={key} value={value}>
{content}
</OptionComponent>
);
};

function GridEditSingleSelectCell(props: GridRenderEditCellParams & Omit<SelectProps, 'id'>) {
const {
Expand Down Expand Up @@ -51,6 +55,9 @@ function GridEditSingleSelectCell(props: GridRenderEditCellParams & Omit<SelectP
const rootProps = useGridRootProps();
const [open, setOpen] = React.useState(rootProps.editMode === 'cell');

const baseSelectProps = rootProps.componentsProps?.baseSelect || {};
const isSelectNative = baseSelectProps.native ?? false;

let valueOptionsFormatted: Array<ValueOptions>;
if (typeof colDef.valueOptions === 'function') {
valueOptionsFormatted = colDef.valueOptions!({ id, row, field });
Expand All @@ -75,7 +82,9 @@ function GridEditSingleSelectCell(props: GridRenderEditCellParams & Omit<SelectP
const handleChange = async (event: React.KeyboardEvent<HTMLInputElement>) => {
setOpen(false);
const target = event.target as HTMLInputElement;
const isValid = await api.setEditCellValue({ id, field, value: target.value }, event);
// NativeSelect casts the value to a string.
const formattedTargetValue = getValueFromValueOptions(target.value, valueOptionsFormatted);
const isValid = await api.setEditCellValue({ id, field, value: formattedTargetValue }, event);

if (rootProps.experimentalFeatures?.newEditingApi) {
return;
Expand Down Expand Up @@ -134,11 +143,14 @@ function GridEditSingleSelectCell(props: GridRenderEditCellParams & Omit<SelectP
onClose: handleClose,
}}
error={error}
native={isSelectNative}
fullWidth
{...other}
{...rootProps.componentsProps?.baseSelect}
>
{valueOptionsFormatted.map(renderSingleSelectOptions)}
{valueOptionsFormatted.map((valueOptions) =>
renderSingleSelectOptions(valueOptions, isSelectNative ? 'option' : MenuItem),
)}
</rootProps.components.BaseSelect>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const GridPanel = React.forwardRef<HTMLDivElement, GridPanelProps>((props, ref)
modifiers={modifiers}
{...other}
>
<ClickAwayListener onClickAway={handleClickAway}>
<ClickAwayListener mouseEvent="onMouseUp" onClickAway={handleClickAway}>
<GridPaperRoot className={classes.paper} elevation={8} onKeyDown={handleKeyDown}>
{isPlaced && children}
</GridPaperRoot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { unstable_composeClasses as composeClasses } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import MenuItem from '@mui/material/MenuItem';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import { SelectChangeEvent } from '@mui/material/Select';
Expand Down Expand Up @@ -152,6 +153,10 @@ function GridFilterForm(props: GridFilterFormProps) {

const baseFormControlProps = rootProps.componentsProps?.baseFormControl || {};

const baseSelectProps = rootProps.componentsProps?.baseSelect || {};
const isBaseSelectNative = baseSelectProps.native ?? true;
const OptionComponent = isBaseSelectNative ? 'option' : MenuItem;

const sortedFilterableColumns = React.useMemo(() => {
switch (columnsSort) {
case 'asc':
Expand Down Expand Up @@ -314,13 +319,13 @@ function GridFilterForm(props: GridFilterFormProps) {
value={multiFilterOperator}
onChange={changeLinkOperator}
disabled={!!disableMultiFilterOperator || linkOperators.length === 1}
native
native={isBaseSelectNative}
{...rootProps.componentsProps?.baseSelect}
>
{linkOperators.map((linkOperator) => (
<option key={linkOperator.toString()} value={linkOperator.toString()}>
<OptionComponent key={linkOperator.toString()} value={linkOperator.toString()}>
{apiRef.current.getLocaleText(getLinkOperatorLocaleKey(linkOperator))}
</option>
</OptionComponent>
))}
</rootProps.components.BaseSelect>
</FilterFormLinkOperatorInput>
Expand All @@ -344,13 +349,13 @@ function GridFilterForm(props: GridFilterFormProps) {
label={apiRef.current.getLocaleText('filterPanelColumns')}
value={item.columnField || ''}
onChange={changeColumn}
native
native={isBaseSelectNative}
{...rootProps.componentsProps?.baseSelect}
>
{sortedFilterableColumns.map((col) => (
<option key={col.field} value={col.field}>
<OptionComponent key={col.field} value={col.field}>
{getColumnLabel(col)}
</option>
</OptionComponent>
))}
</rootProps.components.BaseSelect>
</FilterFormColumnInput>
Expand All @@ -374,17 +379,17 @@ function GridFilterForm(props: GridFilterFormProps) {
id={operatorSelectId}
value={item.operatorValue}
onChange={changeOperator}
native
native={isBaseSelectNative}
inputRef={filterSelectorRef}
{...rootProps.componentsProps?.baseSelect}
>
{currentColumn?.filterOperators?.map((operator) => (
<option key={operator.value} value={operator.value}>
<OptionComponent key={operator.value} value={operator.value}>
{operator.label ||
apiRef.current.getLocaleText(
`filterOperator${capitalize(operator.value)}` as GridTranslationKeys,
)}
</option>
</OptionComponent>
))}
</rootProps.components.BaseSelect>
</FilterFormOperatorInput>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { TextFieldProps } from '@mui/material/TextField';
import MenuItem from '@mui/material/MenuItem';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import { useGridRootProps } from '../../../hooks/utils/useGridRootProps';

Expand All @@ -8,6 +9,10 @@ export function GridFilterInputBoolean(props: GridFilterInputValueProps & TextFi
const [filterValueState, setFilterValueState] = React.useState(item.value || '');
const rootProps = useGridRootProps();

const baseSelectProps = rootProps.componentsProps?.baseSelect || {};
const isSelectNative = baseSelectProps.native ?? true;
const OptionComponent = isSelectNative ? 'option' : MenuItem;

const onFilterChange = React.useCallback(
(event) => {
const value = event.target.value;
Expand All @@ -29,7 +34,9 @@ export function GridFilterInputBoolean(props: GridFilterInputValueProps & TextFi
variant="standard"
select
SelectProps={{
native: true,
native: isSelectNative,
displayEmpty: true,
...rootProps.componentsProps?.baseSelect,
}}
InputLabelProps={{
shrink: true,
Expand All @@ -38,9 +45,13 @@ export function GridFilterInputBoolean(props: GridFilterInputValueProps & TextFi
{...others}
{...rootProps.componentsProps?.baseTextField}
>
<option value="">{apiRef.current.getLocaleText('filterValueAny')}</option>
<option value="true">{apiRef.current.getLocaleText('filterValueTrue')}</option>
<option value="false">{apiRef.current.getLocaleText('filterValueFalse')}</option>
<OptionComponent value="">{apiRef.current.getLocaleText('filterValueAny')}</OptionComponent>
<OptionComponent value="true">
{apiRef.current.getLocaleText('filterValueTrue')}
</OptionComponent>
<OptionComponent value="false">
{apiRef.current.getLocaleText('filterValueFalse')}
</OptionComponent>
</rootProps.components.BaseTextField>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { TextFieldProps } from '@mui/material/TextField';
import { unstable_useId as useId } from '@mui/material/utils';
import MenuItem from '@mui/material/MenuItem';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import { GridColDef } from '../../../models/colDef/gridColDef';
import { GridApiCommunity } from '../../../models/api/gridApiCommunity';
Expand All @@ -11,23 +12,29 @@ import { getValueFromValueOptions } from './filterPanelUtils';
const renderSingleSelectOptions = (
{ valueOptions, valueFormatter, field }: GridColDef,
api: GridApiCommunity,
OptionComponent: React.ElementType,
) => {
const iterableColumnValues =
typeof valueOptions === 'function'
? ['', ...valueOptions({ field })]
: ['', ...(valueOptions || [])];

return iterableColumnValues.map((option) =>
typeof option === 'object' ? (
<option key={option.value} value={option.value}>
{option.label}
</option>
) : (
<option key={option} value={option}>
{valueFormatter && option !== '' ? valueFormatter({ value: option, field, api }) : option}
</option>
),
);
return iterableColumnValues.map((option) => {
const isOptionTypeObject = typeof option === 'object';

const key = isOptionTypeObject ? option.value : option;
const value = isOptionTypeObject ? option.value : option;

const formattedValue =
valueFormatter && option !== '' ? valueFormatter({ value: option, field, api }) : option;
const content = isOptionTypeObject ? option.label : formattedValue;

return (
<OptionComponent key={key} value={value}>
{content}
</OptionComponent>
);
});
};

export type GridFilterInputSingleSelectProps = GridFilterInputValueProps &
Expand All @@ -39,6 +46,9 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
const id = useId();
const rootProps = useGridRootProps();

const baseSelectProps = rootProps.componentsProps?.baseSelect || {};
const isSelectNative = baseSelectProps.native ?? true;

const currentColumn = item.columnField ? apiRef.current.getColumn(item.columnField) : null;

const currentValueOptions = React.useMemo(() => {
Expand Down Expand Up @@ -94,12 +104,17 @@ function GridFilterInputSingleSelect(props: GridFilterInputSingleSelectProps) {
inputRef={focusElementRef}
select
SelectProps={{
native: true,
native: isSelectNative,
...rootProps.componentsProps?.baseSelect,
}}
{...others}
{...rootProps.componentsProps?.baseTextField}
>
{renderSingleSelectOptions(apiRef.current.getColumn(item.columnField), apiRef.current)}
{renderSingleSelectOptions(
apiRef.current.getColumn(item.columnField),
apiRef.current,
isSelectNative ? 'option' : MenuItem,
)}
</rootProps.components.BaseTextField>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { TextFieldProps } from '@mui/material/TextField';
import { unstable_useId as useId } from '@mui/material/utils';
import MenuItem from '@mui/material/MenuItem';
import { GridLoadIcon } from '../../icons';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import { GridColDef } from '../../../models/colDef/gridColDef';
Expand All @@ -24,23 +25,29 @@ function warnDeprecatedTypeSupport(type: string) {
const renderSingleSelectOptions = (
{ valueOptions, valueFormatter, field }: GridColDef,
api: GridApiCommunity,
OptionComponent: React.ElementType,
) => {
const iterableColumnValues =
typeof valueOptions === 'function'
? ['', ...valueOptions({ field })]
: ['', ...(valueOptions || [])];

return iterableColumnValues.map((option) =>
typeof option === 'object' ? (
<option key={option.value} value={option.value}>
{option.label}
</option>
) : (
<option key={option} value={option}>
{valueFormatter && option !== '' ? valueFormatter({ value: option, field, api }) : option}
</option>
),
);
return iterableColumnValues.map((option) => {
const isOptionTypeObject = typeof option === 'object';

const key = isOptionTypeObject ? option.value : option;
const value = isOptionTypeObject ? option.value : option;

const formattedValue =
valueFormatter && option !== '' ? valueFormatter({ value: option, field, api }) : option;
const content = isOptionTypeObject ? option.label : formattedValue;

return (
<OptionComponent key={key} value={value}>
{content}
</OptionComponent>
);
});
};

export const SUBMIT_FILTER_STROKE_TIME = 500;
Expand All @@ -63,16 +70,21 @@ function GridFilterInputValue(props: GridTypeFilterInputValueProps & TextFieldPr
const [applying, setIsApplying] = React.useState(false);
const id = useId();
const rootProps = useGridRootProps();

const baseSelectProps = rootProps.componentsProps?.baseSelect || {};
const isSelectNative = baseSelectProps.native ?? true;

const singleSelectProps: TextFieldProps =
type === 'singleSelect'
? {
select: true,
SelectProps: {
native: true,
...rootProps.componentsProps?.baseSelect,
kyeongsoosoo marked this conversation as resolved.
Show resolved Hide resolved
},
children: renderSingleSelectOptions(
apiRef.current.getColumn(item.columnField),
apiRef.current,
isSelectNative ? 'option' : MenuItem,
),
}
: {};
Expand Down