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] Allow to filter non-filterable columns programmatically #11538

Merged
merged 12 commits into from
Jan 13, 2024
46 changes: 46 additions & 0 deletions docs/data/data-grid/filtering/ReadOnlyFilters.js
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the MIT Grid allows one filter at a max, I have allowed clearing the read-only filter by adding a filter on another column. Users can prevent this by controlling the filter and not updating the filterModel on filterModelChange.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import { DataGrid, GridToolbar } from '@mui/x-data-grid';
import { useDemoData } from '@mui/x-data-grid-generator';

const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin'];

export default function ReadOnlyFilters() {
const { data } = useDemoData({
dataSet: 'Employee',
visibleFields: VISIBLE_FIELDS,
rowLength: 100,
});

const columns = React.useMemo(
() =>
data.columns.map((column) => ({
...column,
filterable: column.field !== 'name',
})),
[data.columns],
);

const [filterModel, setFilterModel] = React.useState({
items: [
{
field: 'name',
operator: 'contains',
value: 'a',
},
],
});

return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid
{...data}
columns={columns}
slots={{
toolbar: GridToolbar,
}}
filterModel={filterModel}
onFilterModelChange={(newFilterModel) => setFilterModel(newFilterModel)}
/>
</div>
);
}
46 changes: 46 additions & 0 deletions docs/data/data-grid/filtering/ReadOnlyFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import { DataGrid, GridFilterModel, GridToolbar } from '@mui/x-data-grid';
import { useDemoData } from '@mui/x-data-grid-generator';

const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin'];

export default function ReadOnlyFilters() {
const { data } = useDemoData({
dataSet: 'Employee',
visibleFields: VISIBLE_FIELDS,
rowLength: 100,
});

const columns = React.useMemo(
() =>
data.columns.map((column) => ({
...column,
filterable: column.field !== 'name',
})),
[data.columns],
);

const [filterModel, setFilterModel] = React.useState<GridFilterModel>({
items: [
{
field: 'name',
operator: 'contains',
value: 'a',
},
],
});

return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid
{...data}
columns={columns}
slots={{
toolbar: GridToolbar,
}}
filterModel={filterModel}
onFilterModelChange={(newFilterModel) => setFilterModel(newFilterModel)}
/>
</div>
);
}
9 changes: 9 additions & 0 deletions docs/data/data-grid/filtering/ReadOnlyFilters.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<DataGrid
{...data}
columns={columns}
slots={{
toolbar: GridToolbar,
}}
filterModel={filterModel}
onFilterModelChange={(newFilterModel) => setFilterModel(newFilterModel)}
/>
20 changes: 20 additions & 0 deletions docs/data/data-grid/filtering/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ You can use the `onFilterModelChange` prop to listen to changes to the filters a

{{"demo": "ControlledFilters.js", "bg": "inline", "defaultCodeOpen": false}}

### Read-only filters

You can initialize the `filterModel`, set the `filterModel` prop, or use `apiRef.current.setFilterModel` to define the filters for columns with `colDef.filterable` set to `false`. These filters will be applied but the user won't be able to change them.
Copy link
Member Author

@MBilalShafi MBilalShafi Jan 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small possible positive side effect would be this kind of filtering will now be possible in read-only mode without tweaking groupingColDef.


```jsx
const columns = [
{ field: 'name', filterable: false },
...otherColumns,
]

<DataGrid
filterModel={{
items: [{ field: 'name', operator: 'contains', value: 'a' }],
}}
columns={columns}
/>
```

{{"demo": "ReadOnlyFilters.js", "bg": "inline", "defaultCodeOpen": false}}

## Disable the filters

### For all columns
Expand Down
4 changes: 1 addition & 3 deletions docs/pages/x/api/data-grid/grid-filter-form.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@
"type": { "name": "arrayOf", "description": "Array&lt;'and'<br>&#124;&nbsp;'or'&gt;" },
"default": "[GridLogicOperator.And, GridLogicOperator.Or]"
},
"multiFilterOperator": {
"type": { "name": "enum", "description": "'and'<br>&#124;&nbsp;'or'" }
},
"operatorInputProps": { "type": { "name": "any" }, "default": "{}" },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"showMultiFilterOperators": { "type": { "name": "bool" } },
"valueInputProps": { "type": { "name": "any" }, "default": "{}" }
},
Expand Down
8 changes: 4 additions & 4 deletions docs/translations/api-docs/data-grid/grid-filter-form.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@
"deprecated": "",
"typeDescriptions": {}
},
"multiFilterOperator": {
"description": "The current logic operator applied.",
"operatorInputProps": {
"description": "Props passed to the operator input component.",
"deprecated": "",
"typeDescriptions": {}
},
"operatorInputProps": {
"description": "Props passed to the operator input component.",
"readOnly": {
"description": "<code>true</code> if the filter is disabled/read only. i.e. <code>colDef.fiterable = false</code> but passed in <code>filterModel</code>",
"deprecated": "",
"typeDescriptions": {}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
GridColDef,
gridVisibleColumnFieldsSelector,
getDataGridUtilityClass,
useGridSelector,
gridFilterModelSelector,
gridFilterableColumnLookupSelector,
} from '@mui/x-data-grid';
import {
GridStateColDef,
Expand Down Expand Up @@ -82,7 +85,7 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe
} = props;

const apiRef = useGridPrivateApiContext();
const columnFields = gridVisibleColumnFieldsSelector(apiRef);
const columnFields = useGridSelector(apiRef, gridVisibleColumnFieldsSelector);
const rootProps = useGridRootProps();
const cellRef = React.useRef<HTMLDivElement>(null);
const handleRef = useForkRef(ref, cellRef);
Expand All @@ -92,9 +95,21 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe
const isEditing = gridHeaderFilteringEditFieldSelector(apiRef) === colDef.field;
const isMenuOpen = gridHeaderFilteringMenuSelector(apiRef) === colDef.field;

const filterModel = useGridSelector(apiRef, gridFilterModelSelector);
const filterableColumnsLookup = useGridSelector(apiRef, gridFilterableColumnLookupSelector);

const isFilterReadOnly = React.useMemo(() => {
if (!filterModel?.items.length) {
return false;
}
const filterModelItem = filterModel.items.find((it) => it.field === colDef.field);
return filterModelItem ? !filterableColumnsLookup[filterModelItem.field] : false;
}, [colDef.field, filterModel, filterableColumnsLookup]);

const currentOperator = filterOperators![0];

const InputComponent = colDef.filterable ? currentOperator!.InputComponent : null;
const InputComponent =
colDef.filterable || isFilterReadOnly ? currentOperator!.InputComponent : null;

const applyFilterChanges = React.useCallback(
(updatedItem: GridFilterItem) => {
Expand Down Expand Up @@ -130,7 +145,7 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe

const onKeyDown = React.useCallback(
(event: React.KeyboardEvent) => {
if (isMenuOpen || isNavigationKey(event.key)) {
if (isMenuOpen || isNavigationKey(event.key) || isFilterReadOnly) {
return;
}
switch (event.key) {
Expand Down Expand Up @@ -172,7 +187,16 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe
break;
}
},
[apiRef, colDef.field, colIndex, columnFields, headerFilterMenuRef, isEditing, isMenuOpen],
[
apiRef,
colDef.field,
colIndex,
columnFields,
headerFilterMenuRef,
isEditing,
isFilterReadOnly,
isMenuOpen,
],
);

const publish = React.useCallback(
Expand Down Expand Up @@ -277,10 +301,13 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe
isFilterActive={isFilterActive}
clearButton={
showClearIcon && isApplied ? (
<GridHeaderFilterClearButton onClick={clearFilterItem} />
<GridHeaderFilterClearButton
onClick={clearFilterItem}
disabled={isFilterReadOnly}
/>
) : null
}
disabled={isNoInputOperator}
disabled={isFilterReadOnly || isNoInputOperator}
tabIndex={-1}
InputLabelProps={null}
sx={colDef.type === 'date' || colDef.type === 'dateTime' ? dateSx : undefined}
Expand All @@ -292,6 +319,7 @@ const GridHeaderFilterCell = React.forwardRef<HTMLDivElement, GridHeaderFilterCe
operators={filterOperators!}
item={item}
field={colDef.field}
disabled={isFilterReadOnly}
applyFilterChanges={applyFilterChanges}
headerFilterMenuRef={headerFilterMenuRef}
buttonRef={buttonRef}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import * as React from 'react';
import { IconButtonProps } from '@mui/material/IconButton';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';

interface GridHeaderFilterClearIconProps {
onClick: () => void;
}
interface GridHeaderFilterClearIconProps extends IconButtonProps {}

const sx = { padding: '2px' };

function GridHeaderFilterClearButton({ onClick }: GridHeaderFilterClearIconProps) {
function GridHeaderFilterClearButton(props: GridHeaderFilterClearIconProps) {
const rootProps = useGridRootProps();
return (
<rootProps.slots.baseIconButton
tabIndex={-1}
aria-label="Clear filter"
size="small"
onClick={onClick}
sx={sx}
{...props}
{...rootProps.slotProps?.baseIconButton}
>
<rootProps.slots.columnMenuClearIcon fontSize="inherit" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@ function GridHeaderFilterMenuContainer(props: {
applyFilterChanges: (item: GridFilterItem) => void;
headerFilterMenuRef: React.MutableRefObject<HTMLButtonElement | null>;
buttonRef: React.Ref<HTMLButtonElement>;
disabled?: boolean;
}) {
const { operators, item, field, buttonRef, headerFilterMenuRef, ...others } = props;
const {
operators,
item,
field,
buttonRef,
headerFilterMenuRef,
disabled = false,
...others
} = props;

const buttonId = useId();
const menuId = useId();
Expand Down Expand Up @@ -58,6 +67,7 @@ function GridHeaderFilterMenuContainer(props: {
size="small"
onClick={handleClick}
sx={sx}
disabled={disabled}
{...rootProps.slotProps?.baseIconButton}
>
<rootProps.slots.openFilterButtonIcon fontSize="small" />
Expand All @@ -83,6 +93,7 @@ GridHeaderFilterMenuContainer.propTypes = {
// ----------------------------------------------------------------------
applyFilterChanges: PropTypes.func.isRequired,
buttonRef: refType,
disabled: PropTypes.bool,
field: PropTypes.string.isRequired,
headerFilterMenuRef: PropTypes.shape({
current: PropTypes.object,
Expand Down
Loading