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
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 @@ -127,6 +127,26 @@ In the example below, the _rating_ column can not be filtered.

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

### Filter non-filterable columns programmatically
Copy link
Member Author

@MBilalShafi MBilalShafi Jan 10, 2024

Choose a reason for hiding this comment

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

Update in context of #11512 (comment)
Sidenote: I've kept internal variable naming to have the keyword readOnly for better understanding. The alternative disabled may also convey not visible at all, so to me readOnly is the more readable naming choice (on a dx perspective).
Let me know if there's a disagreement.


You can initialize the `filterModel`, set the `filterModel` prop, or use the API method `apiRef.current.setFilterModel` to set the filters for non-filterable columns. These filters will be applied but will be read-only on the UI and the user won't be able to change them.

```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}}

## Ignore diacritics (accents)

You can ignore diacritics (accents) when filtering the rows. See [Quick filter - Ignore diacritics (accents)](/x/react-data-grid/filtering/quick-filter/#ignore-diacritics-accents).
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
"description": "Props passed to the logic operator input component."
},
"logicOperators": { "description": "Sets the available logic operators." },
"multiFilterOperator": { "description": "The current logic operator applied." },
"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>"
},
"showMultiFilterOperators": {
"description": "If <code>true</code>, the logic operator field is visible."
},
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