From 79d1961848d82e2b48173f7f0abd8836ff8ed4dd Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 3 Feb 2025 21:26:07 -0500 Subject: [PATCH 1/8] refactor: create base Checkbox --- .../GridCellCheckboxRenderer.tsx | 4 +- .../columnSelection/GridHeaderCheckbox.tsx | 4 +- .../GridColumnsManagement.tsx | 43 +++++++------------ packages/x-data-grid/src/material/index.tsx | 16 ++++++- .../x-data-grid/src/models/gridBaseSlots.ts | 22 ++++++++++ .../src/models/gridSlotsComponentsProps.ts | 2 +- 6 files changed, 60 insertions(+), 31 deletions(-) diff --git a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx index c8b0aa44c7330..dc658f6268942 100644 --- a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -114,7 +114,9 @@ const GridCellCheckboxForwardRef = forwardRef checked={isChecked && !isIndeterminate} onChange={handleChange} className={classes.root} - inputProps={{ 'aria-label': label, name: 'select_all_rows' }} + slotProps={{ + htmlInput: { 'aria-label': label, name: 'select_all_rows' }, + }} tabIndex={tabIndex} onKeyDown={handleKeyDown} disabled={!isMultipleRowSelectionEnabled(rootProps)} diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index 962bf69976ae7..1dd40a3a0e6c1 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import composeClasses from '@mui/utils/composeClasses'; -import FormControlLabel from '@mui/material/FormControlLabel'; import { styled } from '@mui/material/styles'; import { inputBaseClasses } from '@mui/material/InputBase'; import { TextFieldProps } from '../../models/gridBaseSlots'; @@ -272,21 +271,15 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { {currentColumns.map((column) => ( - - } + ))} {currentColumns.length === 0 && ( @@ -298,19 +291,15 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { {(!disableShowHideToggle || !disableResetButton) && currentColumns.length > 0 ? ( {!disableShowHideToggle ? ( - toggleAllColumns(!allHideableColumnsVisible)} - name={apiRef.current.getLocaleText('columnsManagementShowHideAllText')} - sx={{ p: 0.5 }} - {...rootProps.slotProps?.baseCheckbox} - /> - } + toggleAllColumns(!allHideableColumnsVisible)} + name={apiRef.current.getLocaleText('columnsManagementShowHideAllText')} + style={{ padding: 0.5 }} label={apiRef.current.getLocaleText('columnsManagementShowHideAllText')} + {...rootProps.slotProps?.baseCheckbox} /> ) : ( diff --git a/packages/x-data-grid/src/material/index.tsx b/packages/x-data-grid/src/material/index.tsx index 2183e8f16ff94..53bd55c54af4c 100644 --- a/packages/x-data-grid/src/material/index.tsx +++ b/packages/x-data-grid/src/material/index.tsx @@ -11,6 +11,7 @@ import MUIMenuList from '@mui/material/MenuList'; import MUIMenuItem from '@mui/material/MenuItem'; import MUITextField from '@mui/material/TextField'; import MUIFormControl from '@mui/material/FormControl'; +import MUIFormControlLabel from '@mui/material/FormControlLabel'; import MUISelect from '@mui/material/Select'; import MUIButton from '@mui/material/Button'; import MUIIconButton from '@mui/material/IconButton'; @@ -93,7 +94,7 @@ const iconSlots: GridIconSlotsComponent = { const baseSlots: GridBaseSlots = { baseBadge: MUIBadge, - baseCheckbox: MUICheckbox, + baseCheckbox: BaseCheckbox, baseCircularProgress: MUICircularProgress, baseDivider: MUIDivider, baseLinearProgress: MUILinearProgress, @@ -120,6 +121,19 @@ const materialSlots: GridBaseSlots & GridIconSlotsComponent = { export default materialSlots; +function BaseCheckbox(props: GridSlotProps['baseCheckbox']) { + const { label, slotProps, ...other } = props; + if (!label) { + return ; + } + return ( + } + label={label} + /> + ); +} + function BaseMenuItem(props: GridSlotProps['baseMenuItem']) { const { inert, iconStart, iconEnd, children, ...other } = props; if (inert) { diff --git a/packages/x-data-grid/src/models/gridBaseSlots.ts b/packages/x-data-grid/src/models/gridBaseSlots.ts index 694f6e545a2e2..3aa01e02fa374 100644 --- a/packages/x-data-grid/src/models/gridBaseSlots.ts +++ b/packages/x-data-grid/src/models/gridBaseSlots.ts @@ -27,6 +27,28 @@ export type ButtonProps = { touchRippleRef?: any; // FIXME(v8:romgrk): find a way to remove }; +export type CheckboxProps = { + ref?: Ref; + id?: string; + checked?: boolean; + className?: string; + disabled?: boolean; + indeterminate?: boolean; + inputRef?: React.Ref; + name?: string; + label?: React.ReactNode; + onClick?: React.MouseEventHandler; + onChange?: React.ChangeEventHandler; + onKeyDown?: React.KeyboardEventHandler; + size?: 'small' | 'medium'; + slotProps?: { + htmlInput?: React.InputHTMLAttributes; + }; + style?: React.CSSProperties; + tabIndex?: number; + touchRippleRef?: any; // FIXME(v8:romgrk): find a way to remove +}; + export type IconButtonProps = Omit & { label?: string; color?: 'default' | 'inherit' | 'primary'; diff --git a/packages/x-data-grid/src/models/gridSlotsComponentsProps.ts b/packages/x-data-grid/src/models/gridSlotsComponentsProps.ts index cda12ab2fd835..6270b52310284 100644 --- a/packages/x-data-grid/src/models/gridSlotsComponentsProps.ts +++ b/packages/x-data-grid/src/models/gridSlotsComponentsProps.ts @@ -1,7 +1,6 @@ import * as React from 'react'; import type { BadgeProps as MUIBadgeProps } from '@mui/material/Badge'; import type { ButtonProps as MUIButtonProps } from '@mui/material/Button'; -import type { CheckboxProps } from '@mui/material/Checkbox'; import type { CircularProgressProps as MUICircularProgressProps } from '@mui/material/CircularProgress'; import type { LinearProgressProps as MUILinearProgressProps } from '@mui/material/LinearProgress'; import type { MenuListProps } from '@mui/material/MenuList'; @@ -37,6 +36,7 @@ import type { GridColumnHeaderSortIconProps } from '../components/columnHeaders/ import type { BadgeProps, ButtonProps, + CheckboxProps, CircularProgressProps, DividerProps, IconButtonProps, From f98812834c27384c54b05526021cbfd3b449715b Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Wed, 5 Feb 2025 14:38:10 -0500 Subject: [PATCH 2/8] lint --- .../components/columnsManagement/GridColumnsManagement.tsx | 2 ++ packages/x-data-grid/src/material/index.tsx | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index 1dd40a3a0e6c1..44268fb9ede6a 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -272,6 +272,8 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { {currentColumns.map((column) => ( ; + return ; } return ( } label={label} /> From 6530e60a1b12210f9e695c169ac4d2d58e891fd1 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 6 Feb 2025 13:17:33 -0500 Subject: [PATCH 3/8] lint --- .../columnsManagement/GridColumnsManagement.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index 44268fb9ede6a..dbfd2ccbbc92e 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -20,6 +20,10 @@ import { useLazyRef } from '../../hooks/utils/useLazyRef'; import { checkColumnVisibilityModelsSame, defaultSearchPredicate } from './utils'; import { NotRendered } from '../../utils/assert'; +const Checkbox = styled(NotRendered)({ + p: 0.5, +}); + export interface GridColumnsManagementProps { /* * Changes how the options in the columns selector should be ordered. @@ -271,14 +275,14 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { {currentColumns.map((column) => ( - 0 ? ( {!disableShowHideToggle ? ( - toggleAllColumns(!allHideableColumnsVisible)} name={apiRef.current.getLocaleText('columnsManagementShowHideAllText')} - style={{ padding: 0.5 }} label={apiRef.current.getLocaleText('columnsManagementShowHideAllText')} {...rootProps.slotProps?.baseCheckbox} /> From 89ec973019db1baa000c9208665d41981646506d Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Fri, 7 Feb 2025 13:22:37 -0500 Subject: [PATCH 4/8] lint --- .../components/columnsManagement/GridColumnsManagement.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index dbfd2ccbbc92e..23762071b2ecf 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -20,9 +20,7 @@ import { useLazyRef } from '../../hooks/utils/useLazyRef'; import { checkColumnVisibilityModelsSame, defaultSearchPredicate } from './utils'; import { NotRendered } from '../../utils/assert'; -const Checkbox = styled(NotRendered)({ - p: 0.5, -}); +const Checkbox = styled(NotRendered)({}); export interface GridColumnsManagementProps { /* @@ -285,6 +283,7 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { name={column.field} inputRef={isFirstHideableColumn(column) ? firstSwitchRef : undefined} label={column.headerName || column.field} + size='small' {...rootProps.slotProps?.baseCheckbox} /> ))} From 7dbadb826f11c3da764add0289e305515d4ef531 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 10 Feb 2025 15:56:13 -0500 Subject: [PATCH 5/8] fix: autoFocus & density --- .../GridCellCheckboxRenderer.tsx | 26 +--------- .../GridColumnsManagement.tsx | 4 +- packages/x-data-grid/src/material/index.tsx | 51 +++++++++++++++++-- .../x-data-grid/src/models/gridBaseSlots.ts | 3 ++ 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx index dc658f6268942..523bac623d133 100644 --- a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -1,9 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { - unstable_composeClasses as composeClasses, - unstable_useForkRef as useForkRef, -} from '@mui/utils'; +import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -26,10 +23,6 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -interface TouchRippleActions { - stop: (event: any, callback?: () => void) => void; -} - const GridCellCheckboxForwardRef = forwardRef( function GridCellCheckboxRenderer(props, ref) { const { @@ -50,10 +43,6 @@ const GridCellCheckboxForwardRef = forwardRef(null); - - const rippleRef = React.useRef(null); - const handleRef = useForkRef(checkboxElement, ref); const handleChange = (event: React.ChangeEvent) => { const params: GridRowSelectionCheckboxParams = { value: event.target.checked, id }; @@ -69,16 +58,6 @@ const GridCellCheckboxForwardRef = forwardRef { - if (hasFocus) { - const input = checkboxElement.current?.querySelector('input'); - input?.focus({ preventScroll: true }); - } else if (rippleRef.current) { - // Only available in @mui/material v5.4.1 or later - rippleRef.current.stop({}); - } - }, [hasFocus]); - const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => { if (event.key === ' ') { // We call event.stopPropagation to avoid selecting the row and also scrolling to bottom @@ -120,10 +99,9 @@ const GridCellCheckboxForwardRef = forwardRef ); }, diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index c18fd31198170..32233637add24 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -280,7 +280,9 @@ function GridColumnsManagement(props: GridColumnsManagementProps) { name={column.field} inputRef={isFirstHideableColumn(column) ? firstSwitchRef : undefined} label={column.headerName || column.field} - size='medium' + size="medium" + density="compact" + fullWidth {...rootProps.slotProps?.baseCheckbox} /> ))} diff --git a/packages/x-data-grid/src/material/index.tsx b/packages/x-data-grid/src/material/index.tsx index 6baaf33891664..35ae3ddf119ba 100644 --- a/packages/x-data-grid/src/material/index.tsx +++ b/packages/x-data-grid/src/material/index.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import useForkRef from '@mui/utils/useForkRef'; +import { TouchRippleActions } from '@mui/material/ButtonBase/TouchRipple'; import MUIBadge from '@mui/material/Badge'; import MUICheckbox from '@mui/material/Checkbox'; import MUIChip from '@mui/material/Chip'; @@ -94,7 +96,7 @@ const iconSlots: GridIconSlotsComponent = { const baseSlots: GridBaseSlots = { baseBadge: MUIBadge, - baseCheckbox: BaseCheckbox, + baseCheckbox: React.forwardRef(BaseCheckbox), baseCircularProgress: MUICircularProgress, baseDivider: MUIDivider, baseLinearProgress: MUILinearProgress, @@ -121,16 +123,55 @@ const materialSlots: GridBaseSlots & GridIconSlotsComponent = { export default materialSlots; -function BaseCheckbox(props: GridSlotProps['baseCheckbox']) { - const { label, slotProps, className, ...other } = props; +const CHECKBOX_COMPACT = { p: 0.5 }; + +function BaseCheckbox(props: GridSlotProps['baseCheckbox'], ref: React.Ref) { + const { autoFocus, label, fullWidth, slotProps, className, density, ...other } = props; + + const elementRef = React.useRef(null); + const handleRef = useForkRef(elementRef, ref); + const rippleRef = React.useRef(null); + + const sx = density === 'compact' ? CHECKBOX_COMPACT : undefined; + + React.useEffect(() => { + if (autoFocus) { + const input = elementRef.current?.querySelector('input'); + input?.focus({ preventScroll: true }); + } else if (autoFocus === false && rippleRef.current) { + // Only available in @mui/material v5.4.1 or later + // @ts-ignore + rippleRef.current.stop({}); + } + }, [autoFocus]); + if (!label) { - return ; + return ( + + ); } + return ( } + control={ + + } label={label} + sx={fullWidth ? { width: '100%', margin: 0 } : undefined} /> ); } diff --git a/packages/x-data-grid/src/models/gridBaseSlots.ts b/packages/x-data-grid/src/models/gridBaseSlots.ts index de0e1b486596f..5dea8083ccf3a 100644 --- a/packages/x-data-grid/src/models/gridBaseSlots.ts +++ b/packages/x-data-grid/src/models/gridBaseSlots.ts @@ -30,9 +30,11 @@ export type ButtonProps = { export type CheckboxProps = { ref?: Ref; id?: string; + autoFocus?: boolean; checked?: boolean; className?: string; disabled?: boolean; + fullWidth?: boolean; indeterminate?: boolean; inputRef?: React.Ref; name?: string; @@ -41,6 +43,7 @@ export type CheckboxProps = { onChange?: React.ChangeEventHandler; onKeyDown?: React.KeyboardEventHandler; size?: 'small' | 'medium'; + density?: 'standard' | 'compact'; slotProps?: { htmlInput?: React.InputHTMLAttributes; }; From 39a95f9eb8c31a7ed462544272ad15f51e8b0ba4 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 10 Feb 2025 16:32:47 -0500 Subject: [PATCH 6/8] lint --- packages/x-data-grid/src/material/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/x-data-grid/src/material/index.tsx b/packages/x-data-grid/src/material/index.tsx index aa45be23552a8..01af9aa6cbb19 100644 --- a/packages/x-data-grid/src/material/index.tsx +++ b/packages/x-data-grid/src/material/index.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import useForkRef from '@mui/utils/useForkRef'; -import { TouchRippleActions } from '@mui/material/ButtonBase/TouchRipple'; import MUIBadge from '@mui/material/Badge'; import MUICheckbox from '@mui/material/Checkbox'; import MUIChip from '@mui/material/Chip'; @@ -130,7 +129,7 @@ function BaseCheckbox(props: GridSlotProps['baseCheckbox'], ref: React.Ref(null); const handleRef = useForkRef(elementRef, ref); - const rippleRef = React.useRef(null); + const rippleRef = React.useRef(null); const sx = density === 'compact' ? CHECKBOX_COMPACT : undefined; From 5a7d4ed634e6bfb5c873266cc76b9c83598ac237 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 10 Feb 2025 16:37:06 -0500 Subject: [PATCH 7/8] fix: style --- .../components/columnsManagement/GridColumnsManagement.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx index 32233637add24..cc1989a9fdac7 100644 --- a/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx +++ b/packages/x-data-grid/src/components/columnsManagement/GridColumnsManagement.tsx @@ -419,9 +419,8 @@ GridColumnsManagement.propTypes = { const GridColumnsManagementBody = styled('div', { name: 'MuiDataGrid', slot: 'ColumnsManagement', - overridesResolver: (props, styles) => styles.columnsManagement, })<{ ownerState: OwnerState }>(({ theme }) => ({ - padding: theme.spacing(0, 3, 1.5), + padding: theme.spacing(0, 2, 1.5), display: 'flex', flexDirection: 'column', overflow: 'auto', @@ -433,7 +432,6 @@ const GridColumnsManagementBody = styled('div', { const GridColumnsManagementHeader = styled('div', { name: 'MuiDataGrid', slot: 'ColumnsManagementHeader', - overridesResolver: (props, styles) => styles.columnsManagementHeader, })<{ ownerState: OwnerState }>(({ theme }) => ({ padding: theme.spacing(1.5, 3), })); @@ -441,7 +439,6 @@ const GridColumnsManagementHeader = styled('div', { const SearchInput = styled(NotRendered, { name: 'MuiDataGrid', slot: 'ColumnsManagementSearchInput', - overridesResolver: (props, styles) => styles.columnsManagementSearchInput, })<{ ownerState: OwnerState }>(({ theme }) => ({ [`& .${inputBaseClasses.root}`]: { padding: theme.spacing(0, 1.5, 0, 1.5), @@ -458,7 +455,6 @@ const SearchInput = styled(NotRendered, { const GridColumnsManagementFooter = styled('div', { name: 'MuiDataGrid', slot: 'ColumnsManagementFooter', - overridesResolver: (props, styles) => styles.columnsManagementFooter, })<{ ownerState: OwnerState }>(({ theme }) => ({ padding: theme.spacing(0.5, 1, 0.5, 3), display: 'flex', From 7c41e720fbd9ef49a03966211d74e0a59e598f17 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 10 Feb 2025 17:24:49 -0500 Subject: [PATCH 8/8] lint --- packages/x-data-grid/src/material/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/x-data-grid/src/material/index.tsx b/packages/x-data-grid/src/material/index.tsx index 01af9aa6cbb19..85e99346849c1 100644 --- a/packages/x-data-grid/src/material/index.tsx +++ b/packages/x-data-grid/src/material/index.tsx @@ -54,6 +54,8 @@ import type { GridBaseSlots } from '../models/gridSlotsComponent'; import type { GridSlotProps } from '../models/gridSlotsComponentsProps'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +/* eslint-disable material-ui/disallow-react-api-in-server-components */ + const iconSlots: GridIconSlotsComponent = { booleanCellTrueIcon: GridCheckIcon, booleanCellFalseIcon: GridCloseIcon,