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

[core] Create dedicated InputComponents for single select and date filters #3227

Merged
merged 13 commits into from
Nov 29, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import TextField, { TextFieldProps } from '@mui/material/TextField';
import { unstable_useId as useId } from '@mui/material/utils';
import { GridLoadIcon } from '../../icons/index';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';

export interface GridTypeFilterInputDateProps extends GridFilterInputValueProps {
type?: 'date' | 'datetime-local';
}

export const SUBMIT_FILTER_STROKE_TIME = 500;
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved

function GridFilterInputDate(props: GridTypeFilterInputDateProps & TextFieldProps) {
const { item, applyValue, type, apiRef, focusElementRef, ...others } = props;
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved

const filterTimeout = React.useRef<any>();
const [filterValueState, setFilterValueState] = React.useState(item.value ?? '');
const [applying, setIsApplying] = React.useState(false);
const id = useId();

const onFilterChange = React.useCallback(
(event) => {
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
const value = event.target.value;

clearTimeout(filterTimeout.current);
setFilterValueState(String(value));

setIsApplying(true);
filterTimeout.current = setTimeout(() => {
applyValue({ ...item, value });
setIsApplying(false);
}, SUBMIT_FILTER_STROKE_TIME);
},
[applyValue, item],
);

React.useEffect(() => {
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
const itemValue = item.value ?? '';
setFilterValueState(String(itemValue));
}, [item.value]);

const InputProps = {
...(applying ? { endAdornment: <GridLoadIcon /> } : {}),
max: type === 'datetime-local' ? '9999-12-31T23:59' : '9999-12-31',
...others.InputProps,
};

return (
<TextField
id={id}
label={apiRef.current.getLocaleText('filterPanelInputLabel')}
placeholder={apiRef.current.getLocaleText('filterPanelInputPlaceholder')}
value={filterValueState}
onChange={onFilterChange}
type={type || 'text'}
variant="standard"
InputLabelProps={{
shrink: true,
}}
inputRef={focusElementRef}
{...others}
InputProps={InputProps}
/>
);
}

GridFilterInputDate.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
apiRef: PropTypes.any.isRequired,
applyValue: PropTypes.func.isRequired,
focusElementRef: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
PropTypes.func,
PropTypes.object,
]),
item: PropTypes.shape({
columnField: PropTypes.string.isRequired,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
operatorValue: PropTypes.string,
value: PropTypes.any,
}).isRequired,
} as any;

export { GridFilterInputDate };
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import TextField, { TextFieldProps } from '@mui/material/TextField';
import { unstable_useId as useId } from '@mui/material/utils';
import { GridLoadIcon } from '../../icons/index';
import { GridFilterInputValueProps } from './GridFilterInputValueProps';
import { GridColDef } from '../../../models/colDef/gridColDef';

const renderSingleSelectOptions = ({ valueOptions, valueFormatter, field }: GridColDef, api) => {
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
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>
),
);
};

export interface GridTypeFilterInputSingleSelectProps extends GridFilterInputValueProps {
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
type?: 'singleSelect';
}

function GridFilterInputSingleSelect(props: GridTypeFilterInputSingleSelectProps & TextFieldProps) {
const { item, applyValue, type, apiRef, focusElementRef, ...others } = props;
const [filterValueState, setFilterValueState] = React.useState(item.value ?? '');
const [applying, setIsApplying] = React.useState(false);
const id = useId();

const onFilterChange = React.useCallback(
(event) => {
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
let value = event.target.value;
// NativeSelect casts the value to a string.
const column = apiRef.current.getColumn(item.columnField);
const columnValueOptions =
typeof column.valueOptions === 'function'
? column.valueOptions({ field: column.field })
: column.valueOptions;
value = columnValueOptions
.map((option) => (typeof option === 'object' ? option.value : option))
.find((optionValue) => String(optionValue) === value);

setFilterValueState(String(value));

setIsApplying(true);
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
applyValue({ ...item, value });
setIsApplying(false);
},
[apiRef, applyValue, item],
);

React.useEffect(() => {
const itemValue = item.value ?? '';
setFilterValueState(String(itemValue));
}, [item.value]);

const InputProps = applying ? { endAdornment: <GridLoadIcon /> } : others.InputProps;

return (
<TextField
id={id}
label={apiRef.current.getLocaleText('filterPanelInputLabel')}
placeholder={apiRef.current.getLocaleText('filterPanelInputPlaceholder')}
value={filterValueState}
onChange={onFilterChange}
type={type || 'text'}
variant="standard"
InputProps={InputProps}
InputLabelProps={{
shrink: true,
}}
inputRef={focusElementRef}
select
SelectProps={{
native: true,
}}
{...others}
>
{renderSingleSelectOptions(apiRef.current.getColumn(item.columnField), apiRef.current)}
</TextField>
);
}

GridFilterInputSingleSelect.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
apiRef: PropTypes.any.isRequired,
applyValue: PropTypes.func.isRequired,
focusElementRef: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
PropTypes.func,
PropTypes.object,
]),
item: PropTypes.shape({
columnField: PropTypes.string.isRequired,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
operatorValue: PropTypes.string,
value: PropTypes.any,
}).isRequired,
} as any;

export { GridFilterInputSingleSelect };
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ export interface GridTypeFilterInputValueProps extends GridFilterInputValueProps

function GridFilterInputValue(props: GridTypeFilterInputValueProps & TextFieldProps) {
const { item, applyValue, type, apiRef, focusElementRef, ...others } = props;
if (
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
process.env.NODE_ENV !== 'production' &&
['date', 'datetime-local', 'singleSelect'].includes(type as string)
) {
console.warn(
[
'MUI: the use of GridFilterInputValue is deprecated for singleSelect column',
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
'Use GridFilterInputSingleSelect instead.',
].join('\n'),
);
}
const filterTimeout = React.useRef<any>();
const [filterValueState, setFilterValueState] = React.useState(item.value ?? '');
const [applying, setIsApplying] = React.useState(false);
Expand Down
14 changes: 7 additions & 7 deletions packages/grid/_modules_/grid/models/colDef/gridDateOperators.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GridFilterInputValue } from '../../components/panel/filterPanel/GridFilterInputValue';
import { GridFilterInputDate } from '../../components/panel/filterPanel/GridFilterInputDate';
import { GridFilterItem } from '../gridFilterItem';
import { GridFilterOperator } from '../gridFilterOperator';

Expand Down Expand Up @@ -50,31 +50,31 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator[] =
getApplyFilterFn: (filterItem: GridFilterItem) => {
return buildApplyFilterFn(filterItem, (value1, value2) => value1 === value2, showTime);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
value: 'not',
getApplyFilterFn: (filterItem: GridFilterItem) => {
return buildApplyFilterFn(filterItem, (value1, value2) => value1 !== value2, showTime);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
value: 'after',
getApplyFilterFn: (filterItem: GridFilterItem) => {
return buildApplyFilterFn(filterItem, (value1, value2) => value1 > value2, showTime);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
value: 'onOrAfter',
getApplyFilterFn: (filterItem: GridFilterItem) => {
return buildApplyFilterFn(filterItem, (value1, value2) => value1 >= value2, showTime);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
Expand All @@ -87,15 +87,15 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator[] =
!showTime,
);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
value: 'onOrBefore',
getApplyFilterFn: (filterItem: GridFilterItem) => {
return buildApplyFilterFn(filterItem, (value1, value2) => value1 <= value2, showTime);
},
InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputDate,
InputComponentProps: { type: showTime ? 'datetime-local' : 'date' },
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GridFilterInputValue } from '../../components/panel/filterPanel/GridFilterInputValue';
// import { GridFilterInputValue } from '../../components/panel/filterPanel/GridFilterInputValue';
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
import { GridFilterInputSingleSelect } from '../../components/panel/filterPanel/GridFilterInputSingleSelect';
import { GridFilterItem } from '../gridFilterItem';
import { GridFilterOperator } from '../gridFilterOperator';

Expand All @@ -16,7 +17,8 @@ export const getGridSingleSelectOperators: () => GridFilterOperator[] = () => [
return filterItem.value === value;
};
},
InputComponent: GridFilterInputValue,
// InputComponent: GridFilterInputValue,
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
InputComponent: GridFilterInputSingleSelect,
InputComponentProps: { type: 'singleSelect' },
alexfauquette marked this conversation as resolved.
Show resolved Hide resolved
},
{
Expand All @@ -32,7 +34,8 @@ export const getGridSingleSelectOperators: () => GridFilterOperator[] = () => [
return filterItem.value !== value;
};
},
InputComponent: GridFilterInputValue,
// InputComponent: GridFilterInputValue,
InputComponent: GridFilterInputSingleSelect,
InputComponentProps: { type: 'singleSelect' },
},
];