-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DataGridPro] Filtering on Column Header (#7760)
- Loading branch information
1 parent
d199f47
commit bb3a96c
Showing
121 changed files
with
3,744 additions
and
208 deletions.
There are no files selected for viewing
124 changes: 124 additions & 0 deletions
124
docs/data/data-grid/filtering/CustomHeaderFilterDataGridPro.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Button from '@mui/material/Button'; | ||
import Stack from '@mui/material/Stack'; | ||
import { | ||
DataGridPro, | ||
useGridSelector, | ||
gridFilterModelSelector, | ||
useGridApiContext, | ||
} from '@mui/x-data-grid-pro'; | ||
import { useDemoData } from '@mui/x-data-grid-generator'; | ||
|
||
function CustomHeaderFilter(props) { | ||
const { colDef, width, height, hasFocus, colIndex, tabIndex } = props; | ||
const apiRef = useGridApiContext(); | ||
const cellRef = React.useRef(null); | ||
|
||
React.useLayoutEffect(() => { | ||
if (hasFocus && cellRef.current) { | ||
const focusableElement = cellRef.current.querySelector('[tabindex="0"]'); | ||
const elementToFocus = focusableElement || cellRef.current; | ||
elementToFocus?.focus(); | ||
} | ||
}, [apiRef, hasFocus]); | ||
|
||
const publish = React.useCallback( | ||
(eventName, propHandler) => (event) => { | ||
apiRef.current.publishEvent( | ||
eventName, | ||
apiRef.current.getColumnHeaderParams(colDef.field), | ||
event, | ||
); | ||
|
||
if (propHandler) { | ||
propHandler(event); | ||
} | ||
}, | ||
[apiRef, colDef.field], | ||
); | ||
|
||
const onMouseDown = React.useCallback( | ||
(event) => { | ||
if (!hasFocus) { | ||
cellRef.current?.focus(); | ||
apiRef.current.setColumnHeaderFilterFocus(colDef.field, event); | ||
} | ||
}, | ||
[apiRef, colDef.field, hasFocus], | ||
); | ||
|
||
const mouseEventsHandlers = React.useMemo( | ||
() => ({ | ||
onKeyDown: publish('headerFilterKeyDown'), | ||
onClick: publish('headerFilterClick'), | ||
onMouseDown: publish('headerFilterMouseDown', onMouseDown), | ||
}), | ||
[publish, onMouseDown], | ||
); | ||
|
||
const filterModel = useGridSelector(apiRef, gridFilterModelSelector); | ||
const activeFiltersCount = | ||
filterModel.items?.filter(({ field }) => field === colDef.field).length ?? 0; | ||
|
||
return ( | ||
<Stack | ||
sx={{ outline: hasFocus ? 'solid #1976d2 1px' : '' }} | ||
tabIndex={tabIndex} | ||
ref={cellRef} | ||
data-field={colDef.field} | ||
width={width} | ||
height={height} | ||
justifyContent="center" | ||
alignItems="center" | ||
role="columnheader" | ||
aria-colindex={colIndex + 1} | ||
aria-label={colDef.headerName ?? colDef.field} | ||
{...mouseEventsHandlers} | ||
> | ||
<Button | ||
centerRipple={false} | ||
onClick={() => apiRef.current.showFilterPanel(colDef.field)} | ||
> | ||
{activeFiltersCount > 0 ? `${activeFiltersCount} active` : 'Add'} filters | ||
</Button> | ||
</Stack> | ||
); | ||
} | ||
|
||
CustomHeaderFilter.propTypes = { | ||
colDef: PropTypes.object.isRequired, | ||
colIndex: PropTypes.number.isRequired, | ||
hasFocus: PropTypes.bool, | ||
height: PropTypes.number.isRequired, | ||
tabIndex: PropTypes.oneOf([-1, 0]).isRequired, | ||
width: PropTypes.number.isRequired, | ||
}; | ||
|
||
export default function CustomHeaderFilterDataGridPro() { | ||
const { data } = useDemoData({ | ||
dataSet: 'Employee', | ||
rowLength: 100, | ||
}); | ||
|
||
return ( | ||
<div style={{ height: 400, width: '100%' }}> | ||
<DataGridPro | ||
{...data} | ||
initialState={{ | ||
...data.initialState, | ||
columns: { | ||
columnVisibilityModel: { | ||
avatar: false, | ||
id: false, | ||
}, | ||
}, | ||
}} | ||
slots={{ | ||
headerFilterCell: CustomHeaderFilter, | ||
}} | ||
unstable_headerFilters | ||
/> | ||
</div> | ||
); | ||
} |
121 changes: 121 additions & 0 deletions
121
docs/data/data-grid/filtering/CustomHeaderFilterDataGridPro.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import * as React from 'react'; | ||
import Button from '@mui/material/Button'; | ||
import Stack from '@mui/material/Stack'; | ||
import { | ||
DataGridPro, | ||
useGridSelector, | ||
gridFilterModelSelector, | ||
useGridApiContext, | ||
GridHeaderFilterCellProps, | ||
GridHeaderFilterEventLookup, | ||
} from '@mui/x-data-grid-pro'; | ||
import { useDemoData } from '@mui/x-data-grid-generator'; | ||
|
||
function CustomHeaderFilter(props: GridHeaderFilterCellProps) { | ||
const { colDef, width, height, hasFocus, colIndex, tabIndex } = props; | ||
const apiRef = useGridApiContext(); | ||
const cellRef = React.useRef<HTMLDivElement>(null); | ||
|
||
React.useLayoutEffect(() => { | ||
if (hasFocus && cellRef.current) { | ||
const focusableElement = | ||
cellRef.current!.querySelector<HTMLElement>('[tabindex="0"]'); | ||
const elementToFocus = focusableElement || cellRef.current; | ||
elementToFocus?.focus(); | ||
} | ||
}, [apiRef, hasFocus]); | ||
|
||
const publish = React.useCallback( | ||
( | ||
eventName: keyof GridHeaderFilterEventLookup, | ||
propHandler?: React.EventHandler<any>, | ||
) => | ||
(event: React.SyntheticEvent) => { | ||
apiRef.current.publishEvent( | ||
eventName, | ||
apiRef.current.getColumnHeaderParams(colDef.field), | ||
event as any, | ||
); | ||
|
||
if (propHandler) { | ||
propHandler(event); | ||
} | ||
}, | ||
[apiRef, colDef.field], | ||
); | ||
|
||
const onMouseDown = React.useCallback( | ||
(event: React.MouseEvent) => { | ||
if (!hasFocus) { | ||
cellRef.current?.focus(); | ||
apiRef.current.setColumnHeaderFilterFocus(colDef.field, event); | ||
} | ||
}, | ||
[apiRef, colDef.field, hasFocus], | ||
); | ||
|
||
const mouseEventsHandlers = React.useMemo( | ||
() => ({ | ||
onKeyDown: publish('headerFilterKeyDown'), | ||
onClick: publish('headerFilterClick'), | ||
onMouseDown: publish('headerFilterMouseDown', onMouseDown), | ||
}), | ||
[publish, onMouseDown], | ||
); | ||
|
||
const filterModel = useGridSelector(apiRef, gridFilterModelSelector); | ||
const activeFiltersCount = | ||
filterModel.items?.filter(({ field }) => field === colDef.field).length ?? 0; | ||
|
||
return ( | ||
<Stack | ||
sx={{ outline: hasFocus ? 'solid #1976d2 1px' : '' }} | ||
tabIndex={tabIndex} | ||
ref={cellRef} | ||
data-field={colDef.field} | ||
width={width} | ||
height={height} | ||
justifyContent="center" | ||
alignItems="center" | ||
role="columnheader" | ||
aria-colindex={colIndex + 1} | ||
aria-label={colDef.headerName ?? colDef.field} | ||
{...mouseEventsHandlers} | ||
> | ||
<Button | ||
centerRipple={false} | ||
onClick={() => apiRef.current.showFilterPanel(colDef.field)} | ||
> | ||
{activeFiltersCount > 0 ? `${activeFiltersCount} active` : 'Add'} filters | ||
</Button> | ||
</Stack> | ||
); | ||
} | ||
|
||
export default function CustomHeaderFilterDataGridPro() { | ||
const { data } = useDemoData({ | ||
dataSet: 'Employee', | ||
rowLength: 100, | ||
}); | ||
|
||
return ( | ||
<div style={{ height: 400, width: '100%' }}> | ||
<DataGridPro | ||
{...data} | ||
initialState={{ | ||
...data.initialState, | ||
columns: { | ||
columnVisibilityModel: { | ||
avatar: false, | ||
id: false, | ||
}, | ||
}, | ||
}} | ||
slots={{ | ||
headerFilterCell: CustomHeaderFilter, | ||
}} | ||
unstable_headerFilters | ||
/> | ||
</div> | ||
); | ||
} |
16 changes: 16 additions & 0 deletions
16
docs/data/data-grid/filtering/CustomHeaderFilterDataGridPro.tsx.preview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<DataGridPro | ||
{...data} | ||
initialState={{ | ||
...data.initialState, | ||
columns: { | ||
columnVisibilityModel: { | ||
avatar: false, | ||
id: false, | ||
}, | ||
}, | ||
}} | ||
slots={{ | ||
headerFilterCell: CustomHeaderFilter, | ||
}} | ||
unstable_headerFilters | ||
/> |
119 changes: 119 additions & 0 deletions
119
docs/data/data-grid/filtering/CustomHeaderFilterOperatorDataGridPro.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Box from '@mui/material/Box'; | ||
import Rating from '@mui/material/Rating'; | ||
import { DataGridPro, getGridNumericOperators } from '@mui/x-data-grid-pro'; | ||
import { useDemoData } from '@mui/x-data-grid-generator'; | ||
|
||
const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; | ||
|
||
function RatingInputValue(props) { | ||
const { item, applyValue, focusElementRef, headerFilterMenu, clearButton } = props; | ||
|
||
const ratingRef = React.useRef(null); | ||
React.useImperativeHandle(focusElementRef, () => ({ | ||
focus: () => { | ||
ratingRef.current | ||
.querySelector(`input[value="${Number(item.value) || ''}"]`) | ||
.focus(); | ||
}, | ||
})); | ||
|
||
const handleFilterChange = (event, newValue) => { | ||
applyValue({ ...item, value: newValue }); | ||
}; | ||
|
||
return ( | ||
<React.Fragment> | ||
{headerFilterMenu} | ||
<Box | ||
sx={{ | ||
display: 'inline-flex', | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
height: '100%', | ||
pl: '10px', | ||
bl: '1px solid lightgrey', | ||
}} | ||
> | ||
<Rating | ||
name="custom-rating-filter-operator" | ||
placeholder="Filter value" | ||
value={Number(item.value)} | ||
onChange={handleFilterChange} | ||
precision={0.5} | ||
ref={ratingRef} | ||
/> | ||
</Box> | ||
{clearButton} | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
RatingInputValue.propTypes = { | ||
applyValue: PropTypes.func.isRequired, | ||
clearButton: PropTypes.node, | ||
focusElementRef: PropTypes.oneOfType([ | ||
PropTypes.func, | ||
PropTypes.shape({ | ||
current: PropTypes.any.isRequired, | ||
}), | ||
]), | ||
headerFilterMenu: PropTypes.node, | ||
item: PropTypes.shape({ | ||
/** | ||
* The column from which we want to filter the rows. | ||
*/ | ||
field: PropTypes.string.isRequired, | ||
/** | ||
* Must be unique. | ||
* Only useful when the model contains several items. | ||
*/ | ||
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
/** | ||
* The name of the operator we want to apply. | ||
*/ | ||
operator: PropTypes.string.isRequired, | ||
/** | ||
* The filtering value. | ||
* The operator filtering function will decide for each row if the row values is correct compared to this value. | ||
*/ | ||
value: PropTypes.any, | ||
}).isRequired, | ||
}; | ||
|
||
export default function CustomHeaderFilterOperatorDataGridPro() { | ||
const { data } = useDemoData({ | ||
dataSet: 'Employee', | ||
rowLength: 100, | ||
visibleFields: VISIBLE_FIELDS, | ||
}); | ||
|
||
const columns = React.useMemo( | ||
() => | ||
data.columns.map((colDef) => { | ||
if (colDef.field === 'rating') { | ||
return { | ||
...colDef, | ||
minWidth: 200, | ||
filterOperators: getGridNumericOperators() | ||
.filter((operator) => operator.value !== 'isAnyOf') | ||
.map((operator) => ({ | ||
...operator, | ||
InputComponent: operator.InputComponent | ||
? RatingInputValue | ||
: undefined, | ||
})), | ||
}; | ||
} | ||
return colDef; | ||
}), | ||
[data.columns], | ||
); | ||
|
||
return ( | ||
<div style={{ height: 400, width: '100%' }}> | ||
<DataGridPro {...data} columns={columns} unstable_headerFilters /> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.