diff --git a/docs/scripts/api/buildGridSelectorsDocumentation.ts b/docs/scripts/api/buildGridSelectorsDocumentation.ts index c64dfd8d221de..cf36d28a50517 100644 --- a/docs/scripts/api/buildGridSelectorsDocumentation.ts +++ b/docs/scripts/api/buildGridSelectorsDocumentation.ts @@ -64,7 +64,7 @@ export default async function buildGridSelectorsDocumentation( isSelector = true; } else if ( // Selector wrapped in `createSelector` - type.symbol.name === 'OutputSelector' + type.symbol?.name === 'OutputSelector' ) { isSelector = true; supportsApiRef = true; diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 5b02b06b2d42f..c91e68a462c5a 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -16,6 +16,7 @@ import { PropValidator, validateProps, } from '@mui/x-data-grid-pro/internals'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useDataGridPremiumComponent } from './useDataGridPremiumComponent'; import { DataGridPremiumProcessedProps, @@ -42,7 +43,7 @@ if (process.env.NODE_ENV !== 'production') { dataGridPremiumPropValidators = [...propValidatorsDataGrid, ...propValidatorsDataGridPro]; } -const DataGridPremiumRaw = React.forwardRef(function DataGridPremium( +const DataGridPremiumRaw = forwardRef(function DataGridPremium( inProps: DataGridPremiumProps, ref: React.Ref, ) { @@ -59,8 +60,8 @@ const DataGridPremiumRaw = React.forwardRef(function DataGridPremium diff --git a/packages/x-data-grid-premium/src/components/GridPremiumColumnMenu.tsx b/packages/x-data-grid-premium/src/components/GridPremiumColumnMenu.tsx index edaee7957fc32..0849d72352871 100644 --- a/packages/x-data-grid-premium/src/components/GridPremiumColumnMenu.tsx +++ b/packages/x-data-grid-premium/src/components/GridPremiumColumnMenu.tsx @@ -6,6 +6,7 @@ import { GRID_COLUMN_MENU_SLOT_PROPS, GridColumnMenuItemProps, } from '@mui/x-data-grid-pro'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridColumnMenuAggregationItem } from './GridColumnMenuAggregationItem'; import { isGroupingColumn } from '../hooks/features/rowGrouping'; import { GridColumnMenuRowGroupItem } from './GridColumnMenuRowGroupItem'; @@ -32,14 +33,14 @@ export const GRID_COLUMN_MENU_SLOT_PROPS_PREMIUM = { columnMenuGroupingItem: { displayOrder: 27 }, }; -export const GridPremiumColumnMenu = React.forwardRef( +export const GridPremiumColumnMenu = forwardRef( function GridPremiumColumnMenuSimple(props, ref) { return ( ); }, diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 214ab5380f3eb..bbb6ac6472ec9 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -11,6 +11,7 @@ import { GridValidRowModel, } from '@mui/x-data-grid'; import { validateProps } from '@mui/x-data-grid/internals'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useDataGridProComponent } from './useDataGridProComponent'; import { DataGridProProps } from '../models/dataGridProProps'; import { useDataGridProProps } from './useDataGridProProps'; @@ -29,7 +30,7 @@ const configuration = { }; const releaseInfo = getReleaseInfo(); -const DataGridProRaw = React.forwardRef(function DataGridPro( +const DataGridProRaw = forwardRef(function DataGridPro( inProps: DataGridProProps, ref: React.Ref, ) { @@ -46,8 +47,8 @@ const DataGridProRaw = React.forwardRef(function DataGridPro diff --git a/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx b/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx index ea0327ece85e1..9d404a177cef1 100644 --- a/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx +++ b/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { styled } from '@mui/material/styles'; import { GridBaseColumnHeaders, UseGridColumnHeadersProps } from '@mui/x-data-grid/internals'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridColumnHeaders } from '../hooks/features/columnHeaders/useGridColumnHeaders'; const Filler = styled('div')({ @@ -13,7 +14,7 @@ export interface GridColumnHeadersProps extends React.HTMLAttributes, UseGridColumnHeadersProps {} -const GridColumnHeaders = React.forwardRef( +const GridColumnHeaders = forwardRef( function GridColumnHeaders(props, ref) { const { style, @@ -50,7 +51,7 @@ const GridColumnHeaders = React.forwardRef + {getColumnGroupHeadersRows()} {getColumnHeadersRow()} {getColumnFiltersRow()} diff --git a/packages/x-data-grid-pro/src/components/GridProColumnMenu.tsx b/packages/x-data-grid-pro/src/components/GridProColumnMenu.tsx index c35f78686ea3c..3b065c0fd5749 100644 --- a/packages/x-data-grid-pro/src/components/GridProColumnMenu.tsx +++ b/packages/x-data-grid-pro/src/components/GridProColumnMenu.tsx @@ -5,6 +5,7 @@ import { GRID_COLUMN_MENU_SLOTS, GRID_COLUMN_MENU_SLOT_PROPS, } from '@mui/x-data-grid'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridColumnMenuPinningItem } from './GridColumnMenuPinningItem'; export const GRID_COLUMN_MENU_SLOTS_PRO = { @@ -19,14 +20,14 @@ export const GRID_COLUMN_MENU_SLOT_PROPS_PRO = { }, }; -export const GridProColumnMenu = React.forwardRef( +export const GridProColumnMenu = forwardRef( function GridProColumnMenu(props, ref) { return ( ); }, diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index bd8aca4630ed1..6edafa1c1eb66 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -33,6 +33,7 @@ import { shouldCellShowLeftBorder, shouldCellShowRightBorder, } from '@mui/x-data-grid/internals'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { DataGridProProcessedProps } from '../../models/dataGridProProps'; import { GridHeaderFilterMenuContainer } from './GridHeaderFilterMenuContainer'; @@ -101,302 +102,300 @@ const defaultInputComponents: { [key in GridColType]: React.JSXElementConstructo actions: null, custom: null, }; -const GridHeaderFilterCell = React.forwardRef( - (props, ref) => { - const { - colIndex, - height, - hasFocus, - width, - headerClassName, - colDef, - item, - headerFilterMenuRef, - InputComponentProps, - showClearIcon = true, - pinnedPosition, - style: styleProp, - indexInSection, - sectionLength, - gridHasFiller, - ...other - } = props; - - const apiRef = useGridPrivateApiContext(); - const columnFields = useGridSelector(apiRef, gridVisibleColumnFieldsSelector); - const rootProps = useGridRootProps(); - const cellRef = React.useRef(null); - const handleRef = useForkRef(ref, cellRef); - const inputRef = React.useRef(null); - const buttonRef = React.useRef(null); - - const editingField = useGridSelector(apiRef, gridHeaderFilteringEditFieldSelector); - const isEditing = editingField === colDef.field; - - const menuOpenField = useGridSelector(apiRef, gridHeaderFilteringMenuSelector); - const isMenuOpen = menuOpenField === colDef.field; - - // TODO: Support for `isAnyOf` operator - const filterOperators = React.useMemo(() => { - if (!colDef.filterOperators) { - return []; +const GridHeaderFilterCell = forwardRef((props, ref) => { + const { + colIndex, + height, + hasFocus, + width, + headerClassName, + colDef, + item, + headerFilterMenuRef, + InputComponentProps, + showClearIcon = true, + pinnedPosition, + style: styleProp, + indexInSection, + sectionLength, + gridHasFiller, + ...other + } = props; + + const apiRef = useGridPrivateApiContext(); + const columnFields = useGridSelector(apiRef, gridVisibleColumnFieldsSelector); + const rootProps = useGridRootProps(); + const cellRef = React.useRef(null); + const handleRef = useForkRef(ref, cellRef); + const inputRef = React.useRef(null); + const buttonRef = React.useRef(null); + + const editingField = useGridSelector(apiRef, gridHeaderFilteringEditFieldSelector); + const isEditing = editingField === colDef.field; + + const menuOpenField = useGridSelector(apiRef, gridHeaderFilteringMenuSelector); + const isMenuOpen = menuOpenField === colDef.field; + + // TODO: Support for `isAnyOf` operator + const filterOperators = React.useMemo(() => { + if (!colDef.filterOperators) { + return []; + } + return colDef.filterOperators.filter((operator) => operator.value !== 'isAnyOf'); + }, [colDef.filterOperators]); + 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 = React.useMemo( + () => + filterOperators.find((operator) => operator.value === item.operator) ?? filterOperators![0], + [item.operator, filterOperators], + ); + + const InputComponent = + colDef.filterable || isFilterReadOnly + ? (currentOperator.InputComponent ?? defaultInputComponents[colDef.type as GridColType]) + : null; + + const applyFilterChanges = React.useCallback( + (updatedItem: GridFilterItem) => { + if (item.value && updatedItem.value === undefined) { + apiRef.current.deleteFilterItem(updatedItem); + return; } - return colDef.filterOperators.filter((operator) => operator.value !== 'isAnyOf'); - }, [colDef.filterOperators]); - const filterModel = useGridSelector(apiRef, gridFilterModelSelector); - const filterableColumnsLookup = useGridSelector(apiRef, gridFilterableColumnLookupSelector); - - const isFilterReadOnly = React.useMemo(() => { - if (!filterModel?.items.length) { - return false; + apiRef.current.upsertFilterItem(updatedItem); + }, + [apiRef, item], + ); + + const clearFilterItem = React.useCallback(() => { + apiRef.current.deleteFilterItem(item); + }, [apiRef, item]); + + let headerFilterComponent: React.ReactNode; + if (colDef.renderHeaderFilter) { + headerFilterComponent = colDef.renderHeaderFilter({ ...props, inputRef }); + } + + React.useLayoutEffect(() => { + if (hasFocus && !isMenuOpen) { + let focusableElement = cellRef.current!.querySelector('[tabindex="0"]'); + if (isEditing && InputComponent) { + focusableElement = inputRef.current; + } + const elementToFocus = focusableElement || cellRef.current; + elementToFocus?.focus(); + if (apiRef.current.columnHeadersContainerRef.current) { + apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0; } - const filterModelItem = filterModel.items.find((it) => it.field === colDef.field); - return filterModelItem ? !filterableColumnsLookup[filterModelItem.field] : false; - }, [colDef.field, filterModel, filterableColumnsLookup]); - - const currentOperator = React.useMemo( - () => - filterOperators.find((operator) => operator.value === item.operator) ?? filterOperators![0], - [item.operator, filterOperators], - ); - - const InputComponent = - colDef.filterable || isFilterReadOnly - ? (currentOperator.InputComponent ?? defaultInputComponents[colDef.type as GridColType]) - : null; - - const applyFilterChanges = React.useCallback( - (updatedItem: GridFilterItem) => { - if (item.value && updatedItem.value === undefined) { - apiRef.current.deleteFilterItem(updatedItem); - return; - } - apiRef.current.upsertFilterItem(updatedItem); - }, - [apiRef, item], - ); - - const clearFilterItem = React.useCallback(() => { - apiRef.current.deleteFilterItem(item); - }, [apiRef, item]); - - let headerFilterComponent: React.ReactNode; - if (colDef.renderHeaderFilter) { - headerFilterComponent = colDef.renderHeaderFilter({ ...props, inputRef }); } + }, [InputComponent, apiRef, hasFocus, isEditing, isMenuOpen]); - React.useLayoutEffect(() => { - if (hasFocus && !isMenuOpen) { - let focusableElement = cellRef.current!.querySelector('[tabindex="0"]'); - if (isEditing && InputComponent) { - focusableElement = inputRef.current; - } - const elementToFocus = focusableElement || cellRef.current; - elementToFocus?.focus(); - if (apiRef.current.columnHeadersContainerRef.current) { - apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0; - } + const onKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (isMenuOpen || isNavigationKey(event.key) || isFilterReadOnly) { + return; } - }, [InputComponent, apiRef, hasFocus, isEditing, isMenuOpen]); - - const onKeyDown = React.useCallback( - (event: React.KeyboardEvent) => { - if (isMenuOpen || isNavigationKey(event.key) || isFilterReadOnly) { - return; - } - switch (event.key) { - case 'Escape': - if (isEditing) { + switch (event.key) { + case 'Escape': + if (isEditing) { + apiRef.current.stopHeaderFilterEditMode(); + } + break; + case 'Enter': + if (isEditing) { + if (!event.defaultPrevented) { apiRef.current.stopHeaderFilterEditMode(); - } - break; - case 'Enter': - if (isEditing) { - if (!event.defaultPrevented) { - apiRef.current.stopHeaderFilterEditMode(); - break; - } - } - if (event.metaKey || event.ctrlKey) { - headerFilterMenuRef.current = buttonRef.current; - apiRef.current.showHeaderFilterMenu(colDef.field); break; } - apiRef.current.startHeaderFilterEditMode(colDef.field); - break; - case 'Tab': { - if (isEditing) { - const fieldToFocus = columnFields[colIndex + (event.shiftKey ? -1 : 1)] ?? null; - - if (fieldToFocus) { - apiRef.current.startHeaderFilterEditMode(fieldToFocus); - apiRef.current.setColumnHeaderFilterFocus(fieldToFocus, event); - } - } + } + if (event.metaKey || event.ctrlKey) { + headerFilterMenuRef.current = buttonRef.current; + apiRef.current.showHeaderFilterMenu(colDef.field); break; } - default: - if (isEditing || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) { - break; + apiRef.current.startHeaderFilterEditMode(colDef.field); + break; + case 'Tab': { + if (isEditing) { + const fieldToFocus = columnFields[colIndex + (event.shiftKey ? -1 : 1)] ?? null; + + if (fieldToFocus) { + apiRef.current.startHeaderFilterEditMode(fieldToFocus); + apiRef.current.setColumnHeaderFilterFocus(fieldToFocus, event); } - apiRef.current.startHeaderFilterEditMode(colDef.field); + } + break; + } + default: + if (isEditing || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) { break; + } + apiRef.current.startHeaderFilterEditMode(colDef.field); + break; + } + }, + [ + apiRef, + colDef.field, + colIndex, + columnFields, + headerFilterMenuRef, + isEditing, + isFilterReadOnly, + isMenuOpen, + ], + ); + + const publish = React.useCallback( + (eventName: keyof GridHeaderFilterEventLookup, propHandler?: React.EventHandler) => + (event: React.SyntheticEvent) => { + apiRef.current.publishEvent( + eventName, + apiRef.current.getColumnHeaderParams(colDef.field), + event as any, + ); + + if (propHandler) { + propHandler(event); } }, - [ - apiRef, - colDef.field, - colIndex, - columnFields, - headerFilterMenuRef, - isEditing, - isFilterReadOnly, - isMenuOpen, - ], - ); + [apiRef, colDef.field], + ); + + const onMouseDown = React.useCallback( + (event: React.MouseEvent) => { + if (!hasFocus) { + if (inputRef.current?.contains?.(event.target as HTMLElement)) { + inputRef.current.focus(); + } + apiRef.current.setColumnHeaderFilterFocus(colDef.field, event); + } + }, + [apiRef, colDef.field, hasFocus], + ); + + const mouseEventsHandlers = React.useMemo( + () => ({ + onKeyDown: publish('headerFilterKeyDown', onKeyDown), + onClick: publish('headerFilterClick'), + onMouseDown: publish('headerFilterMouseDown', onMouseDown), + onBlur: publish('headerFilterBlur'), + }), + [onMouseDown, onKeyDown, publish], + ); + + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + gridHasFiller, + ); + + const ownerState: OwnerState = { + ...rootProps, + pinnedPosition, + colDef, + showLeftBorder, + showRightBorder, + }; - const publish = React.useCallback( - (eventName: keyof GridHeaderFilterEventLookup, propHandler?: React.EventHandler) => - (event: React.SyntheticEvent) => { - apiRef.current.publishEvent( - eventName, - apiRef.current.getColumnHeaderParams(colDef.field), - event as any, - ); - - if (propHandler) { - propHandler(event); - } - }, - [apiRef, colDef.field], - ); + const classes = useUtilityClasses(ownerState as OwnerState); - const onMouseDown = React.useCallback( - (event: React.MouseEvent) => { - if (!hasFocus) { - if (inputRef.current?.contains?.(event.target as HTMLElement)) { - inputRef.current.focus(); - } - apiRef.current.setColumnHeaderFilterFocus(colDef.field, event); - } - }, - [apiRef, colDef.field, hasFocus], - ); + const isNoInputOperator = currentOperator.requiresFilterValue === false; - const mouseEventsHandlers = React.useMemo( - () => ({ - onKeyDown: publish('headerFilterKeyDown', onKeyDown), - onClick: publish('headerFilterClick'), - onMouseDown: publish('headerFilterMouseDown', onMouseDown), - onBlur: publish('headerFilterBlur'), - }), - [onMouseDown, onKeyDown, publish], - ); + const isApplied = item?.value !== undefined || isNoInputOperator; - const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); - const showRightBorder = shouldCellShowRightBorder( - pinnedPosition, - indexInSection, - sectionLength, - rootProps.showCellVerticalBorder, - gridHasFiller, + const label = + currentOperator.headerLabel ?? + apiRef.current.getLocaleText( + `headerFilterOperator${capitalize(item.operator)}` as 'headerFilterOperatorContains', ); - const ownerState: OwnerState = { - ...rootProps, - pinnedPosition, - colDef, - showLeftBorder, - showRightBorder, - }; - - const classes = useUtilityClasses(ownerState as OwnerState); - - const isNoInputOperator = currentOperator.requiresFilterValue === false; - - const isApplied = item?.value !== undefined || isNoInputOperator; - - const label = - currentOperator.headerLabel ?? - apiRef.current.getLocaleText( - `headerFilterOperator${capitalize(item.operator)}` as 'headerFilterOperatorContains', - ); - - const isFilterActive = isApplied || hasFocus; - - return ( -
- {headerFilterComponent} - {InputComponent && headerFilterComponent === undefined ? ( - - apiRef.current.startHeaderFilterEditMode(colDef.field)} - onBlur={(event: React.FocusEvent) => { - apiRef.current.stopHeaderFilterEditMode(); - // Blurring an input element should reset focus state only if `relatedTarget` is not the header filter cell - if (!event.relatedTarget?.className.includes('columnHeader')) { - apiRef.current.setState((state) => ({ - ...state, - focus: { - cell: null, - columnHeader: null, - columnHeaderFilter: null, - columnGroupHeader: null, - }, - })); - } - }} - label={capitalize(label)} - placeholder="" - isFilterActive={isFilterActive} - clearButton={ - showClearIcon && isApplied ? ( - - ) : null + const isFilterActive = isApplied || hasFocus; + + return ( +
+ {headerFilterComponent} + {InputComponent && headerFilterComponent === undefined ? ( + + apiRef.current.startHeaderFilterEditMode(colDef.field)} + onBlur={(event: React.FocusEvent) => { + apiRef.current.stopHeaderFilterEditMode(); + // Blurring an input element should reset focus state only if `relatedTarget` is not the header filter cell + if (!event.relatedTarget?.className.includes('columnHeader')) { + apiRef.current.setState((state) => ({ + ...state, + focus: { + cell: null, + columnHeader: null, + columnHeaderFilter: null, + columnGroupHeader: null, + }, + })); } - disabled={isFilterReadOnly || isNoInputOperator} - tabIndex={-1} - InputLabelProps={null} - sx={colDef.type === 'date' || colDef.type === 'dateTime' ? dateSx : undefined} - {...(isNoInputOperator ? { value: '' } : {})} - {...currentOperator?.InputComponentProps} - {...InputComponentProps} - /> - - - ) : null} -
- ); - }, -); + }} + label={capitalize(label)} + placeholder="" + isFilterActive={isFilterActive} + clearButton={ + showClearIcon && isApplied ? ( + + ) : null + } + disabled={isFilterReadOnly || isNoInputOperator} + tabIndex={-1} + InputLabelProps={null} + sx={colDef.type === 'date' || colDef.type === 'dateTime' ? dateSx : undefined} + {...(isNoInputOperator ? { value: '' } : {})} + {...currentOperator?.InputComponentProps} + {...InputComponentProps} + /> + +
+ ) : null} +
+ ); +}); GridHeaderFilterCell.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index 5a5384eeff74f..0fc08b33366f3 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -1,6 +1,7 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridBody, GridFooterPlaceholder, GridHeader, GridRoot } from '../components'; import { useGridAriaAttributes } from '../hooks/utils/useGridAriaAttributes'; import { useGridRowAriaAttributes } from '../hooks/features/rows/useGridRowAriaAttributes'; @@ -42,7 +43,7 @@ if (process.env.NODE_ENV !== 'production') { ]; } -const DataGridRaw = React.forwardRef(function DataGrid( +const DataGridRaw = forwardRef(function DataGrid( inProps: DataGridProps, ref: React.Ref, ) { @@ -58,8 +59,8 @@ const DataGridRaw = React.forwardRef(function DataGrid diff --git a/packages/x-data-grid/src/components/GridColumnHeaders.tsx b/packages/x-data-grid/src/components/GridColumnHeaders.tsx index 7ff4b81617fe7..075ced6aa3a63 100644 --- a/packages/x-data-grid/src/components/GridColumnHeaders.tsx +++ b/packages/x-data-grid/src/components/GridColumnHeaders.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { fastMemo } from '@mui/x-internals/fastMemo'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridColumnHeaders, UseGridColumnHeadersProps, @@ -13,7 +14,7 @@ export interface GridColumnHeadersProps ref?: React.Ref; } -const GridColumnHeaders = React.forwardRef( +const GridColumnHeaders = forwardRef( function GridColumnHeaders(props, ref) { const { className, @@ -48,7 +49,7 @@ const GridColumnHeaders = React.forwardRef + {getColumnGroupHeadersRows()} {getColumnHeadersRow()} diff --git a/packages/x-data-grid/src/components/GridFooter.tsx b/packages/x-data-grid/src/components/GridFooter.tsx index 8f8ee151fee26..6fa0cc0778f88 100644 --- a/packages/x-data-grid/src/components/GridFooter.tsx +++ b/packages/x-data-grid/src/components/GridFooter.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridSelector } from '../hooks/utils/useGridSelector'; import { gridTopLevelRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/rowSelection/gridRowSelectionSelector'; @@ -9,7 +10,7 @@ import { GridSelectedRowCount } from './GridSelectedRowCount'; import { GridFooterContainer, GridFooterContainerProps } from './containers/GridFooterContainer'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -const GridFooter = React.forwardRef( +const GridFooter = forwardRef( function GridFooter(props, ref) { const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); @@ -40,7 +41,7 @@ const GridFooter = React.forwardRef( ); return ( - + {selectedRowCountElement} {rowCountElement} {paginationElement} diff --git a/packages/x-data-grid/src/components/GridLoadingOverlay.tsx b/packages/x-data-grid/src/components/GridLoadingOverlay.tsx index a5a9377f04179..e07bc9923adab 100644 --- a/packages/x-data-grid/src/components/GridLoadingOverlay.tsx +++ b/packages/x-data-grid/src/components/GridLoadingOverlay.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import type { DataGridProcessedProps } from '../models/props/DataGridProps'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { GridOverlay, GridOverlayProps } from './containers/GridOverlay'; @@ -43,7 +44,7 @@ const LOADING_VARIANTS: Record< }, }; -const GridLoadingOverlay = React.forwardRef( +const GridLoadingOverlay = forwardRef( function GridLoadingOverlay(props, ref) { const { variant = 'linear-progress', noRowsVariant = 'skeleton', style, ...other } = props; const apiRef = useGridApiContext(); @@ -53,7 +54,7 @@ const GridLoadingOverlay = React.forwardRef + ); diff --git a/packages/x-data-grid/src/components/GridNoResultsOverlay.tsx b/packages/x-data-grid/src/components/GridNoResultsOverlay.tsx index 747e26dbae177..1e7f3e2f13bdb 100644 --- a/packages/x-data-grid/src/components/GridNoResultsOverlay.tsx +++ b/packages/x-data-grid/src/components/GridNoResultsOverlay.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { GridOverlay, GridOverlayProps } from './containers/GridOverlay'; -export const GridNoResultsOverlay = React.forwardRef( +export const GridNoResultsOverlay = forwardRef( function GridNoResultsOverlay(props, ref) { const apiRef = useGridApiContext(); const noResultsOverlayLabel = apiRef.current.getLocaleText('noResultsOverlayLabel'); return ( - + {noResultsOverlayLabel} ); diff --git a/packages/x-data-grid/src/components/GridNoRowsOverlay.tsx b/packages/x-data-grid/src/components/GridNoRowsOverlay.tsx index 1d27e2b081a1d..562fa69473128 100644 --- a/packages/x-data-grid/src/components/GridNoRowsOverlay.tsx +++ b/packages/x-data-grid/src/components/GridNoRowsOverlay.tsx @@ -1,15 +1,16 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { GridOverlay, GridOverlayProps } from './containers/GridOverlay'; -const GridNoRowsOverlay = React.forwardRef( +const GridNoRowsOverlay = forwardRef( function GridNoRowsOverlay(props, ref) { const apiRef = useGridApiContext(); const noRowsLabel = apiRef.current.getLocaleText('noRowsLabel'); return ( - + {noRowsLabel} ); diff --git a/packages/x-data-grid/src/components/GridPagination.tsx b/packages/x-data-grid/src/components/GridPagination.tsx index 909ec8820c410..afbc3f1099f6f 100644 --- a/packages/x-data-grid/src/components/GridPagination.tsx +++ b/packages/x-data-grid/src/components/GridPagination.tsx @@ -6,6 +6,7 @@ import TablePagination, { TablePaginationProps, LabelDisplayedRowsArgs, } from '@mui/material/TablePagination'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridSelector } from '../hooks/utils/useGridSelector'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; @@ -60,7 +61,7 @@ interface GridPaginationOwnProps { component?: React.ElementType; } -const GridPagination = React.forwardRef< +const GridPagination = forwardRef< unknown, Partial< // See https://github.com/mui/material-ui/issues/40427 @@ -158,7 +159,6 @@ const GridPagination = React.forwardRef< return ( ); }); diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx index edca8b2e81ec6..7af29485e080d 100644 --- a/packages/x-data-grid/src/components/GridRow.tsx +++ b/packages/x-data-grid/src/components/GridRow.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; import { fastMemo } from '@mui/x-internals/fastMemo'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridRowEventLookup } from '../models/events'; import { GridRowId, GridRowModel } from '../models/gridRows'; import { GridEditModes, GridRowModes, GridCellModes } from '../models/gridEditRowModel'; @@ -68,7 +69,7 @@ export interface GridRowProps extends React.HTMLAttributes { [x: `data-${string}`]: string; } -const GridRow = React.forwardRef(function GridRow(props, refProp) { +const GridRow = forwardRef(function GridRow(props, refProp) { const { selected, rowId, @@ -444,7 +445,6 @@ const GridRow = React.forwardRef(function GridRow( return (
(function GridRow( {...ariaAttributes} {...eventHandlers} {...other} + ref={handleRef} > {leftCells}
( +const GridRowCount = forwardRef( function GridRowCount(props, ref) { const { className, rowCount, visibleRowCount, ...other } = props; const apiRef = useGridApiContext(); @@ -58,10 +59,10 @@ const GridRowCount = React.forwardRef( return ( {apiRef.current.getLocaleText('footerTotalRows')} {text} diff --git a/packages/x-data-grid/src/components/GridSelectedRowCount.tsx b/packages/x-data-grid/src/components/GridSelectedRowCount.tsx index 17b033fbfeeab..010cd145e4988 100644 --- a/packages/x-data-grid/src/components/GridSelectedRowCount.tsx +++ b/packages/x-data-grid/src/components/GridSelectedRowCount.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { styled, SxProps, Theme } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { getDataGridUtilityClass } from '../constants/gridClasses'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; @@ -47,7 +48,7 @@ const GridSelectedRowCountRoot = styled('div', { }, })); -const GridSelectedRowCount = React.forwardRef( +const GridSelectedRowCount = forwardRef( function GridSelectedRowCount(props, ref) { const { className, selectedRowCount, ...other } = props; const apiRef = useGridApiContext(); @@ -57,10 +58,10 @@ const GridSelectedRowCount = React.forwardRef {rowSelectedText} diff --git a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx index 365b65bfd82d1..afd60790a004f 100644 --- a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx +++ b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx @@ -3,6 +3,7 @@ import clsx from 'clsx'; import { styled } from '@mui/system'; import useForkRef from '@mui/utils/useForkRef'; import composeClasses from '@mui/utils/composeClasses'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { @@ -48,223 +49,224 @@ const useUtilityClasses = (ownerState: OwnerState) => { const getColIndex = (el: HTMLElement) => parseInt(el.getAttribute('data-colindex')!, 10); -const GridSkeletonLoadingOverlay = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(function GridSkeletonLoadingOverlay(props, forwardedRef) { - const rootProps = useGridRootProps(); - const { slots } = rootProps; - const classes = useUtilityClasses({ classes: rootProps.classes }); - const ref = React.useRef(null); - const handleRef = useForkRef(ref, forwardedRef); - const apiRef = useGridApiContext(); - const dimensions = useGridSelector(apiRef, gridDimensionsSelector); - const viewportHeight = dimensions?.viewportInnerSize.height ?? 0; - const skeletonRowsCount = Math.ceil(viewportHeight / dimensions.rowHeight); - const totalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); - const positions = useGridSelector(apiRef, gridColumnPositionsSelector); - const inViewportCount = React.useMemo( - () => positions.filter((value) => value <= totalWidth).length, - [totalWidth, positions], - ); - const allVisibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); - const columns = React.useMemo( - () => allVisibleColumns.slice(0, inViewportCount), - [allVisibleColumns, inViewportCount], - ); - const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector); - - const getPinnedStyle = React.useCallback( - (computedWidth: number, index: number, position: GridPinnedColumnPosition) => { - const pinnedOffset = getPinnedCellOffset( - position, - computedWidth, - index, - positions, - dimensions, - ); - return { [position]: pinnedOffset } as const; - }, - [dimensions, positions], - ); - - const getPinnedPosition = React.useCallback( - (field: string) => { - if (pinnedColumns.left.findIndex((col) => col.field === field) !== -1) { - return GridPinnedColumnPosition.LEFT; - } - if (pinnedColumns.right.findIndex((col) => col.field === field) !== -1) { - return GridPinnedColumnPosition.RIGHT; - } - return undefined; - }, - [pinnedColumns.left, pinnedColumns.right], - ); - - const children = React.useMemo(() => { - const array: React.ReactNode[] = []; - - for (let i = 0; i < skeletonRowsCount; i += 1) { - const rowCells: React.ReactNode[] = []; +const GridSkeletonLoadingOverlay = forwardRef>( + function GridSkeletonLoadingOverlay(props, forwardedRef) { + const rootProps = useGridRootProps(); + const { slots } = rootProps; + const classes = useUtilityClasses({ classes: rootProps.classes }); + const ref = React.useRef(null); + const handleRef = useForkRef(ref, forwardedRef); + const apiRef = useGridApiContext(); + const dimensions = useGridSelector(apiRef, gridDimensionsSelector); + const viewportHeight = dimensions?.viewportInnerSize.height ?? 0; + const skeletonRowsCount = Math.ceil(viewportHeight / dimensions.rowHeight); + const totalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); + const positions = useGridSelector(apiRef, gridColumnPositionsSelector); + const inViewportCount = React.useMemo( + () => positions.filter((value) => value <= totalWidth).length, + [totalWidth, positions], + ); + const allVisibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); + const columns = React.useMemo( + () => allVisibleColumns.slice(0, inViewportCount), + [allVisibleColumns, inViewportCount], + ); + const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector); - for (let colIndex = 0; colIndex < columns.length; colIndex += 1) { - const column = columns[colIndex]; - const pinnedPosition = getPinnedPosition(column.field); - const isPinnedLeft = pinnedPosition === GridPinnedColumnPosition.LEFT; - const isPinnedRight = pinnedPosition === GridPinnedColumnPosition.RIGHT; - const sectionLength = pinnedPosition - ? pinnedColumns[pinnedPosition].length // pinned section - : columns.length - pinnedColumns.left.length - pinnedColumns.right.length; // middle section - const sectionIndex = pinnedPosition - ? pinnedColumns[pinnedPosition].findIndex((col) => col.field === column.field) // pinned section - : colIndex - pinnedColumns.left.length; // middle section - const pinnedStyle = - pinnedPosition && getPinnedStyle(column.computedWidth, colIndex, pinnedPosition); - const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width; - const showRightBorder = shouldCellShowRightBorder( - pinnedPosition, - sectionIndex, - sectionLength, - rootProps.showCellVerticalBorder, - gridHasFiller, + const getPinnedStyle = React.useCallback( + (computedWidth: number, index: number, position: GridPinnedColumnPosition) => { + const pinnedOffset = getPinnedCellOffset( + position, + computedWidth, + index, + positions, + dimensions, ); - const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, sectionIndex); - const isLastColumn = colIndex === columns.length - 1; - const isFirstPinnedRight = isPinnedRight && sectionIndex === 0; - const hasFillerBefore = isFirstPinnedRight && gridHasFiller; - const hasFillerAfter = isLastColumn && !isFirstPinnedRight && gridHasFiller; - const expandedWidth = dimensions.viewportOuterSize.width - dimensions.columnsTotalWidth; - const emptyCellWidth = Math.max(0, expandedWidth); - const emptyCell = ( - - ); - const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; - const hasScrollbarFiller = isLastColumn && scrollbarWidth !== 0; + return { [position]: pinnedOffset } as const; + }, + [dimensions, positions], + ); - if (hasFillerBefore) { - rowCells.push(emptyCell); + const getPinnedPosition = React.useCallback( + (field: string) => { + if (pinnedColumns.left.findIndex((col) => col.field === field) !== -1) { + return GridPinnedColumnPosition.LEFT; } + if (pinnedColumns.right.findIndex((col) => col.field === field) !== -1) { + return GridPinnedColumnPosition.RIGHT; + } + return undefined; + }, + [pinnedColumns.left, pinnedColumns.right], + ); - rowCells.push( - , - ); + const children = React.useMemo(() => { + const array: React.ReactNode[] = []; - if (hasFillerAfter) { - rowCells.push(emptyCell); - } + for (let i = 0; i < skeletonRowsCount; i += 1) { + const rowCells: React.ReactNode[] = []; + + for (let colIndex = 0; colIndex < columns.length; colIndex += 1) { + const column = columns[colIndex]; + const pinnedPosition = getPinnedPosition(column.field); + const isPinnedLeft = pinnedPosition === GridPinnedColumnPosition.LEFT; + const isPinnedRight = pinnedPosition === GridPinnedColumnPosition.RIGHT; + const sectionLength = pinnedPosition + ? pinnedColumns[pinnedPosition].length // pinned section + : columns.length - pinnedColumns.left.length - pinnedColumns.right.length; // middle section + const sectionIndex = pinnedPosition + ? pinnedColumns[pinnedPosition].findIndex((col) => col.field === column.field) // pinned section + : colIndex - pinnedColumns.left.length; // middle section + const pinnedStyle = + pinnedPosition && getPinnedStyle(column.computedWidth, colIndex, pinnedPosition); + const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width; + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + sectionIndex, + sectionLength, + rootProps.showCellVerticalBorder, + gridHasFiller, + ); + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, sectionIndex); + const isLastColumn = colIndex === columns.length - 1; + const isFirstPinnedRight = isPinnedRight && sectionIndex === 0; + const hasFillerBefore = isFirstPinnedRight && gridHasFiller; + const hasFillerAfter = isLastColumn && !isFirstPinnedRight && gridHasFiller; + const expandedWidth = dimensions.viewportOuterSize.width - dimensions.columnsTotalWidth; + const emptyCellWidth = Math.max(0, expandedWidth); + const emptyCell = ( + + ); + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + const hasScrollbarFiller = isLastColumn && scrollbarWidth !== 0; + + if (hasFillerBefore) { + rowCells.push(emptyCell); + } - if (hasScrollbarFiller) { rowCells.push( - 0} + , ); + + if (hasFillerAfter) { + rowCells.push(emptyCell); + } + + if (hasScrollbarFiller) { + rowCells.push( + 0} + />, + ); + } } + + array.push( +
+ {rowCells} +
, + ); } + return array; + }, [ + slots, + columns, + pinnedColumns, + skeletonRowsCount, + rootProps.showCellVerticalBorder, + dimensions.columnsTotalWidth, + dimensions.viewportOuterSize.width, + dimensions.rowHeight, + dimensions.hasScrollY, + dimensions.scrollbarSize, + getPinnedPosition, + getPinnedStyle, + ]); - array.push( -
- {rowCells} -
, + // Sync the column resize of the overlay columns with the grid + const handleColumnResize: GridEventListener<'columnResize'> = (params) => { + const { colDef, width } = params; + const cells = ref.current?.querySelectorAll( + `[data-field="${escapeOperandAttributeSelector(colDef.field)}"]`, ); - } - return array; - }, [ - slots, - columns, - pinnedColumns, - skeletonRowsCount, - rootProps.showCellVerticalBorder, - dimensions.columnsTotalWidth, - dimensions.viewportOuterSize.width, - dimensions.rowHeight, - dimensions.hasScrollY, - dimensions.scrollbarSize, - getPinnedPosition, - getPinnedStyle, - ]); - // Sync the column resize of the overlay columns with the grid - const handleColumnResize: GridEventListener<'columnResize'> = (params) => { - const { colDef, width } = params; - const cells = ref.current?.querySelectorAll( - `[data-field="${escapeOperandAttributeSelector(colDef.field)}"]`, - ); - - if (!cells) { - throw new Error('MUI X: Expected skeleton cells to be defined with `data-field` attribute.'); - } + if (!cells) { + throw new Error( + 'MUI X: Expected skeleton cells to be defined with `data-field` attribute.', + ); + } - const resizedColIndex = columns.findIndex((col) => col.field === colDef.field); - const pinnedPosition = getPinnedPosition(colDef.field); - const isPinnedLeft = pinnedPosition === GridPinnedColumnPosition.LEFT; - const isPinnedRight = pinnedPosition === GridPinnedColumnPosition.RIGHT; - const currentWidth = getComputedStyle(cells[0]).getPropertyValue('--width'); - const delta = parseInt(currentWidth, 10) - width; + const resizedColIndex = columns.findIndex((col) => col.field === colDef.field); + const pinnedPosition = getPinnedPosition(colDef.field); + const isPinnedLeft = pinnedPosition === GridPinnedColumnPosition.LEFT; + const isPinnedRight = pinnedPosition === GridPinnedColumnPosition.RIGHT; + const currentWidth = getComputedStyle(cells[0]).getPropertyValue('--width'); + const delta = parseInt(currentWidth, 10) - width; - if (cells) { - cells.forEach((element) => { - element.style.setProperty('--width', `${width}px`); - }); - } + if (cells) { + cells.forEach((element) => { + element.style.setProperty('--width', `${width}px`); + }); + } - if (isPinnedLeft) { - const pinnedCells = ref.current?.querySelectorAll( - `.${gridClasses['cell--pinnedLeft']}`, - ); - pinnedCells?.forEach((element) => { - const colIndex = getColIndex(element); - if (colIndex > resizedColIndex) { - element.style.left = `${parseInt(getComputedStyle(element).left, 10) - delta}px`; - } - }); - } + if (isPinnedLeft) { + const pinnedCells = ref.current?.querySelectorAll( + `.${gridClasses['cell--pinnedLeft']}`, + ); + pinnedCells?.forEach((element) => { + const colIndex = getColIndex(element); + if (colIndex > resizedColIndex) { + element.style.left = `${parseInt(getComputedStyle(element).left, 10) - delta}px`; + } + }); + } - if (isPinnedRight) { - const pinnedCells = ref.current?.querySelectorAll( - `.${gridClasses['cell--pinnedRight']}`, - ); - pinnedCells?.forEach((element) => { - const colIndex = getColIndex(element); - if (colIndex < resizedColIndex) { - element.style.right = `${parseInt(getComputedStyle(element).right, 10) + delta}px`; - } - }); - } - }; + if (isPinnedRight) { + const pinnedCells = ref.current?.querySelectorAll( + `.${gridClasses['cell--pinnedRight']}`, + ); + pinnedCells?.forEach((element) => { + const colIndex = getColIndex(element); + if (colIndex < resizedColIndex) { + element.style.right = `${parseInt(getComputedStyle(element).right, 10) + delta}px`; + } + }); + } + }; - useGridApiEventHandler(apiRef, 'columnResize', handleColumnResize); + useGridApiEventHandler(apiRef, 'columnResize', handleColumnResize); - return ( - - {children} - - ); -}); + return ( + + {children} + + ); + }, +); export { GridSkeletonLoadingOverlay }; diff --git a/packages/x-data-grid/src/components/cell/GridActionsCellItem.tsx b/packages/x-data-grid/src/components/cell/GridActionsCellItem.tsx index 554926d5227b1..d6df6072a0504 100644 --- a/packages/x-data-grid/src/components/cell/GridActionsCellItem.tsx +++ b/packages/x-data-grid/src/components/cell/GridActionsCellItem.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { IconButtonProps } from '@mui/material/IconButton'; import { MenuItemProps } from '@mui/material/MenuItem'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; interface GridActionsCellItemCommonProps { @@ -25,61 +26,51 @@ export type GridActionsCellItemProps = GridActionsCellItemCommonProps & } & Omit) ); -const GridActionsCellItem = React.forwardRef( - (props, ref) => { - const rootProps = useGridRootProps(); +const GridActionsCellItem = forwardRef((props, ref) => { + const rootProps = useGridRootProps(); - if (!props.showInMenu) { - const { label, icon, showInMenu, onClick, ...other } = props; + if (!props.showInMenu) { + const { label, icon, showInMenu, onClick, ...other } = props; - const handleClick = (event: React.MouseEvent) => { - onClick?.(event); - }; - - return ( - } - size="small" - role="menuitem" - aria-label={label} - {...other} - onClick={handleClick} - {...rootProps.slotProps?.baseIconButton} - > - {React.cloneElement(icon!, { fontSize: 'small' })} - - ); - } - - const { - label, - icon, - showInMenu, - onClick, - closeMenuOnClick = true, - closeMenu, - ...other - } = props; - - const handleClick = (event: React.MouseEvent) => { + const handleClick = (event: React.MouseEvent) => { onClick?.(event); - if (closeMenuOnClick) { - closeMenu?.(); - } }; return ( - } > - {label} - + {React.cloneElement(icon!, { fontSize: 'small' })} + ); - }, -); + } + + const { label, icon, showInMenu, onClick, closeMenuOnClick = true, closeMenu, ...other } = props; + + const handleClick = (event: React.MouseEvent) => { + onClick?.(event); + if (closeMenuOnClick) { + closeMenu?.(); + } + }; + + return ( + + {label} + + ); +}); GridActionsCellItem.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 491b78af2ba26..92ee0efc8f47d 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -9,6 +9,7 @@ import { } from '@mui/utils'; import { fastMemo } from '@mui/x-internals/fastMemo'; import { useRtl } from '@mui/system/RtlProvider'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { doesSupportPreventScroll } from '../../utils/doesSupportPreventScroll'; import { getDataGridUtilityClass, gridClasses } from '../../constants/gridClasses'; import { @@ -152,7 +153,7 @@ let warnedOnce = false; // TODO(v7): Removing the wrapper will break the docs performance visualization demo. -const GridCell = React.forwardRef(function GridCell(props, ref) { +const GridCell = forwardRef(function GridCell(props, ref) { const { column, rowId, @@ -489,7 +490,6 @@ const GridCell = React.forwardRef(function GridCe return (
(function GridCe {...draggableEventHandlers} {...other} onFocus={handleFocus} + ref={handleRef} > {children}
diff --git a/packages/x-data-grid/src/components/cell/GridEditInputCell.tsx b/packages/x-data-grid/src/components/cell/GridEditInputCell.tsx index 67ca09344a00e..e1243ecb269d2 100644 --- a/packages/x-data-grid/src/components/cell/GridEditInputCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridEditInputCell.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; import { unstable_composeClasses as composeClasses, unstable_useEnhancedEffect as useEnhancedEffect, } from '@mui/utils'; import { styled } from '@mui/material/styles'; import InputBase, { InputBaseProps } from '@mui/material/InputBase'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridRenderEditCellParams } from '../../models/params/gridCellParams'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -52,93 +54,156 @@ export interface GridEditInputCellProps ) => Promise | void; } -const GridEditInputCell = React.forwardRef( - (props, ref) => { - const rootProps = useGridRootProps(); - - const { - id, - value, - formattedValue, - api, - field, - row, - rowNode, - colDef, - cellMode, - isEditable, - tabIndex, - hasFocus, - isValidating, - debounceMs = 200, - isProcessingProps, - onValueChange, - ...other - } = props; - - const apiRef = useGridApiContext(); - const inputRef = React.useRef(); - const [valueState, setValueState] = React.useState(value); - const classes = useUtilityClasses(rootProps); - - const handleChange = React.useCallback( - async (event: React.ChangeEvent) => { - const newValue = event.target.value; - - if (onValueChange) { - await onValueChange(event, newValue); - } - - const column = apiRef.current.getColumn(field); - - let parsedValue = newValue; - if (column.valueParser) { - parsedValue = column.valueParser(newValue, apiRef.current.getRow(id), column, apiRef); - } - - setValueState(parsedValue); - apiRef.current.setEditCellValue( - { id, field, value: parsedValue, debounceMs, unstable_skipValueParser: true }, - event, - ); - }, - [apiRef, debounceMs, field, id, onValueChange], - ); - - const meta = apiRef.current.unstable_getEditCellMeta(id, field); - - React.useEffect(() => { - if (meta?.changeReason !== 'debouncedSetEditCellValue') { - setValueState(value); +const GridEditInputCell = forwardRef((props, ref) => { + const rootProps = useGridRootProps(); + + const { + id, + value, + formattedValue, + api, + field, + row, + rowNode, + colDef, + cellMode, + isEditable, + tabIndex, + hasFocus, + isValidating, + debounceMs = 200, + isProcessingProps, + onValueChange, + ...other + } = props; + + const apiRef = useGridApiContext(); + const inputRef = React.useRef(); + const [valueState, setValueState] = React.useState(value); + const classes = useUtilityClasses(rootProps); + + const handleChange = React.useCallback( + async (event: React.ChangeEvent) => { + const newValue = event.target.value; + + if (onValueChange) { + await onValueChange(event, newValue); } - }, [meta, value]); - useEnhancedEffect(() => { - if (hasFocus) { - inputRef.current!.focus(); + const column = apiRef.current.getColumn(field); + + let parsedValue = newValue; + if (column.valueParser) { + parsedValue = column.valueParser(newValue, apiRef.current.getRow(id), column, apiRef); } - }, [hasFocus]); - - return ( - - ) : undefined - } - {...other} - /> - ); - }, -); + + setValueState(parsedValue); + apiRef.current.setEditCellValue( + { id, field, value: parsedValue, debounceMs, unstable_skipValueParser: true }, + event, + ); + }, + [apiRef, debounceMs, field, id, onValueChange], + ); + + const meta = apiRef.current.unstable_getEditCellMeta(id, field); + + React.useEffect(() => { + if (meta?.changeReason !== 'debouncedSetEditCellValue') { + setValueState(value); + } + }, [meta, value]); + + useEnhancedEffect(() => { + if (hasFocus) { + inputRef.current!.focus(); + } + }, [hasFocus]); + + return ( + : undefined + } + {...other} + ref={ref} + /> + ); +}); + +GridEditInputCell.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + /** + * GridApi that let you manipulate the grid. + */ + api: PropTypes.object.isRequired, + /** + * The mode of the cell. + */ + cellMode: PropTypes.oneOf(['edit', 'view']).isRequired, + changeReason: PropTypes.oneOf(['debouncedSetEditCellValue', 'setEditCellValue']), + /** + * The column of the row that the current cell belongs to. + */ + colDef: PropTypes.object.isRequired, + debounceMs: PropTypes.number, + /** + * The column field of the cell that triggered the event. + */ + field: PropTypes.string.isRequired, + /** + * The cell value formatted with the column valueFormatter. + */ + formattedValue: PropTypes.any, + /** + * If true, the cell is the active element. + */ + hasFocus: PropTypes.bool.isRequired, + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * If true, the cell is editable. + */ + isEditable: PropTypes.bool, + isProcessingProps: PropTypes.bool, + isValidating: PropTypes.bool, + /** + * Callback called when the value is changed by the user. + * @param {React.ChangeEvent} event The event source of the callback. + * @param {Date | null} newValue The value that is going to be passed to `apiRef.current.setEditCellValue`. + * @returns {Promise | void} A promise to be awaited before calling `apiRef.current.setEditCellValue` + */ + onValueChange: PropTypes.func, + /** + * The row model of the row that the current cell belongs to. + */ + row: PropTypes.any.isRequired, + /** + * The node of the row that the current cell belongs to. + */ + rowNode: PropTypes.object.isRequired, + /** + * the tabIndex value. + */ + tabIndex: PropTypes.oneOf([-1, 0]).isRequired, + /** + * The cell value. + * If the column has `valueGetter`, use `params.row` to directly access the fields. + */ + value: PropTypes.any, +} as any; export { GridEditInputCell }; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx b/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx index d2d37d0773dee..3e6adf2b0e90b 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { styled, SxProps, Theme } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { DataGridProcessedProps } from '../../models/props/DataGridProps'; @@ -33,7 +34,7 @@ interface GridBaseColumnHeadersProps extends React.HTMLAttributes; } -export const GridBaseColumnHeaders = React.forwardRef( +export const GridBaseColumnHeaders = forwardRef( function GridColumnHeaders(props, ref) { const { className, ...other } = props; const rootProps = useGridRootProps(); @@ -42,11 +43,11 @@ export const GridBaseColumnHeaders = React.forwardRef ); }, diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx index e1761b3f8304b..20ba38a443f14 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { styled } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { isOverflown } from '../../utils/domUtils'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -32,25 +33,24 @@ const GridColumnHeaderTitleRoot = styled('div', { lineHeight: 'normal', }); -const ColumnHeaderInnerTitle = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(function ColumnHeaderInnerTitle(props, ref) { - // Tooltip adds aria-label to the props, which is not needed since the children prop is a string - // See https://github.com/mui/mui-x/pull/14482 - const { className, 'aria-label': ariaLabel, ...other } = props; - const rootProps = useGridRootProps(); - const classes = useUtilityClasses(rootProps); +const ColumnHeaderInnerTitle = forwardRef>( + function ColumnHeaderInnerTitle(props, ref) { + // Tooltip adds aria-label to the props, which is not needed since the children prop is a string + // See https://github.com/mui/mui-x/pull/14482 + const { className, 'aria-label': ariaLabel, ...other } = props; + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); - return ( - - ); -}); + return ( + + ); + }, +); export interface GridColumnHeaderTitleProps { label: string; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx b/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx index 412bb46be9a17..13b41fa523087 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import clsx from 'clsx'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridStateColDef } from '../../models/colDef/gridColDef'; import { GridSortDirection } from '../../models/gridSortModel'; import { useGridPrivateApiContext } from '../../hooks/utils/useGridPrivateApiContext'; @@ -42,103 +43,107 @@ interface GridGenericColumnHeaderItemProps style?: React.CSSProperties; } -const GridGenericColumnHeaderItem = React.forwardRef(function GridGenericColumnHeaderItem( - props: GridGenericColumnHeaderItemProps, - ref, -) { - const { - classes, - columnMenuOpen, - colIndex, - height, - isResizing, - sortDirection, - hasFocus, - tabIndex, - separatorSide, - isDraggable, - headerComponent, - description, - elementId, - width, - columnMenuIconButton = null, - columnMenu = null, - columnTitleIconButtons = null, - headerClassName, - label, - resizable, - draggableContainerProps, - columnHeaderSeparatorProps, - style, - ...other - } = props; +const GridGenericColumnHeaderItem = forwardRef( + function GridGenericColumnHeaderItem(props, ref) { + const { + classes, + columnMenuOpen, + colIndex, + height, + isResizing, + sortDirection, + hasFocus, + tabIndex, + separatorSide, + isDraggable, + headerComponent, + description, + elementId, + width, + columnMenuIconButton = null, + columnMenu = null, + columnTitleIconButtons = null, + headerClassName, + label, + resizable, + draggableContainerProps, + columnHeaderSeparatorProps, + style, + ...other + } = props; - const apiRef = useGridPrivateApiContext(); - const rootProps = useGridRootProps(); - const headerCellRef = React.useRef(null); + const apiRef = useGridPrivateApiContext(); + const rootProps = useGridRootProps(); + const headerCellRef = React.useRef(null); - const handleRef = useForkRef(headerCellRef, ref); + const handleRef = useForkRef(headerCellRef, ref); - let ariaSort: 'ascending' | 'descending' | 'none' = 'none'; - if (sortDirection != null) { - ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending'; - } + let ariaSort: 'ascending' | 'descending' | 'none' = 'none'; + if (sortDirection != null) { + ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending'; + } - React.useLayoutEffect(() => { - const columnMenuState = apiRef.current.state.columnMenu; - if (hasFocus && !columnMenuState.open) { - const focusableElement = headerCellRef.current!.querySelector('[tabindex="0"]'); - const elementToFocus = focusableElement || headerCellRef.current; - elementToFocus?.focus(); - if (apiRef.current.columnHeadersContainerRef?.current) { - apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0; + React.useLayoutEffect(() => { + const columnMenuState = apiRef.current.state.columnMenu; + if (hasFocus && !columnMenuState.open) { + const focusableElement = + headerCellRef.current!.querySelector('[tabindex="0"]'); + const elementToFocus = focusableElement || headerCellRef.current; + elementToFocus?.focus(); + if (apiRef.current.columnHeadersContainerRef?.current) { + apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0; + } } - } - }, [apiRef, hasFocus]); + }, [apiRef, hasFocus]); - return ( -
+ return (
-
-
- {headerComponent !== undefined ? ( - headerComponent - ) : ( - - )} +
+
+
+ {headerComponent !== undefined ? ( + headerComponent + ) : ( + + )} +
+ {columnTitleIconButtons}
- {columnTitleIconButtons} + {columnMenuIconButton}
- {columnMenuIconButton} + + {columnMenu}
- - {columnMenu} -
- ); -}); + ); + }, +); export { GridGenericColumnHeaderItem }; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridIconButtonContainer.tsx b/packages/x-data-grid/src/components/columnHeaders/GridIconButtonContainer.tsx index 7c0c88a6df7b6..7eea1fc1d3015 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridIconButtonContainer.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridIconButtonContainer.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { styled } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; @@ -30,20 +31,19 @@ const GridIconButtonContainerRoot = styled('div', { width: 0, })); -export const GridIconButtonContainer = React.forwardRef< - HTMLDivElement, - GridIconButtonContainerProps ->(function GridIconButtonContainer(props: GridIconButtonContainerProps, ref) { - const { className, ...other } = props; - const rootProps = useGridRootProps(); - const classes = useUtilityClasses(rootProps); - - return ( - - ); -}); +export const GridIconButtonContainer = forwardRef( + function GridIconButtonContainer(props, ref) { + const { className, ...other } = props; + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + + return ( + + ); + }, +); diff --git a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx index 98a8451c56df9..008c1f29aa5f4 100644 --- a/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/x-data-grid/src/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -4,6 +4,7 @@ import { unstable_composeClasses as composeClasses, unstable_useForkRef as useForkRef, } from '@mui/utils'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; @@ -29,7 +30,7 @@ interface TouchRippleActions { stop: (event: any, callback?: () => void) => void; } -const GridCellCheckboxForwardRef = React.forwardRef( +const GridCellCheckboxForwardRef = forwardRef( function GridCellCheckboxRenderer(props, ref) { const { field, @@ -104,7 +105,6 @@ const GridCellCheckboxForwardRef = React.forwardRef ); }, diff --git a/packages/x-data-grid/src/components/columnSelection/GridHeaderCheckbox.tsx b/packages/x-data-grid/src/components/columnSelection/GridHeaderCheckbox.tsx index d20cf2cd870f3..700d9fb6bf38a 100644 --- a/packages/x-data-grid/src/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/x-data-grid/src/components/columnSelection/GridHeaderCheckbox.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import composeClasses from '@mui/utils/composeClasses'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { isMultipleRowSelectionEnabled } from '../../hooks/features/rowSelection/utils'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { gridTabIndexColumnHeaderSelector } from '../../hooks/features/focus/gridFocusStateSelector'; @@ -27,7 +28,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -const GridHeaderCheckbox = React.forwardRef( +const GridHeaderCheckbox = forwardRef( function GridHeaderCheckbox(props, ref) { const { field, colDef, ...other } = props; const [, forceUpdate] = React.useState(false); @@ -137,7 +138,6 @@ const GridHeaderCheckbox = React.forwardRef ); }, diff --git a/packages/x-data-grid/src/components/containers/GridFooterContainer.tsx b/packages/x-data-grid/src/components/containers/GridFooterContainer.tsx index 42f4a1051319f..358699988a724 100644 --- a/packages/x-data-grid/src/components/containers/GridFooterContainer.tsx +++ b/packages/x-data-grid/src/components/containers/GridFooterContainer.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { styled, SxProps, Theme } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; @@ -35,18 +36,18 @@ const GridFooterContainerRoot = styled('div', { borderTop: '1px solid', }); -const GridFooterContainer = React.forwardRef( - function GridFooterContainer(props: GridFooterContainerProps, ref) { +const GridFooterContainer = forwardRef( + function GridFooterContainer(props, ref) { const { className, ...other } = props; const rootProps = useGridRootProps(); const classes = useUtilityClasses(rootProps); return ( ); }, diff --git a/packages/x-data-grid/src/components/containers/GridOverlay.tsx b/packages/x-data-grid/src/components/containers/GridOverlay.tsx index 73a339738ca41..03437f180de05 100644 --- a/packages/x-data-grid/src/components/containers/GridOverlay.tsx +++ b/packages/x-data-grid/src/components/containers/GridOverlay.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; import { Theme, SxProps, styled } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -37,20 +38,17 @@ const GridOverlayRoot = styled('div', { backgroundColor: 'var(--unstable_DataGrid-overlayBackground)', }); -const GridOverlay = React.forwardRef(function GridOverlay( - props: GridOverlayProps, - ref, -) { +const GridOverlay = forwardRef(function GridOverlay(props, ref) { const { className, ...other } = props; const rootProps = useGridRootProps(); const classes = useUtilityClasses(rootProps); return ( ); }); diff --git a/packages/x-data-grid/src/components/containers/GridRoot.tsx b/packages/x-data-grid/src/components/containers/GridRoot.tsx index 7a3c66be2c36a..2d5a538e6f9aa 100644 --- a/packages/x-data-grid/src/components/containers/GridRoot.tsx +++ b/packages/x-data-grid/src/components/containers/GridRoot.tsx @@ -9,6 +9,7 @@ import { } from '@mui/utils'; import { SxProps } from '@mui/system'; import { Theme } from '@mui/material/styles'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { GridRootStyles } from './GridRootStyles'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { useGridPrivateApiContext } from '../../hooks/utils/useGridPrivateApiContext'; @@ -44,7 +45,7 @@ const useUtilityClasses = (ownerState: OwnerState, density: GridDensity) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -const GridRoot = React.forwardRef(function GridRoot(props, ref) { +const GridRoot = forwardRef(function GridRoot(props, ref) { const rootProps = useGridRootProps(); const { className, ...other } = props; const apiRef = useGridPrivateApiContext(); @@ -68,10 +69,10 @@ const GridRoot = React.forwardRef(function GridRo return ( ); }); diff --git a/packages/x-data-grid/src/components/containers/GridToolbarContainer.tsx b/packages/x-data-grid/src/components/containers/GridToolbarContainer.tsx index e797f56e7b5df..e2c43605cfcf0 100644 --- a/packages/x-data-grid/src/components/containers/GridToolbarContainer.tsx +++ b/packages/x-data-grid/src/components/containers/GridToolbarContainer.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import { styled, SxProps, Theme } from '@mui/system'; import composeClasses from '@mui/utils/composeClasses'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -35,7 +36,7 @@ const GridToolbarContainerRoot = styled('div', { padding: theme.spacing(0.5, 0.5, 0), })); -const GridToolbarContainer = React.forwardRef( +const GridToolbarContainer = forwardRef( function GridToolbarContainer(props, ref) { const { className, children, ...other } = props; const rootProps = useGridRootProps(); @@ -46,10 +47,10 @@ const GridToolbarContainer = React.forwardRef {children} diff --git a/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx b/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx index b5a94ecc60df2..0d58d4d3589f1 100644 --- a/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx +++ b/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridColumnMenuSlots } from '../../../hooks/features/columnMenu/useGridColumnMenuSlots'; import { GridColumnMenuContainer } from './GridColumnMenuContainer'; import { GridColumnMenuColumnsItem } from './menuItems/GridColumnMenuColumnsItem'; @@ -20,7 +21,7 @@ export const GRID_COLUMN_MENU_SLOT_PROPS = { columnMenuColumnsItem: { displayOrder: 30 }, }; -const GridGenericColumnMenu = React.forwardRef( +const GridGenericColumnMenu = forwardRef( function GridGenericColumnMenu(props, ref) { const { defaultSlots, defaultSlotProps, slots, slotProps, ...other } = props; @@ -33,7 +34,7 @@ const GridGenericColumnMenu = React.forwardRef + {orderedSlots.map(([Component, otherProps], index) => ( ))} @@ -75,7 +76,7 @@ GridGenericColumnMenu.propTypes = { slots: PropTypes.object, } as any; -const GridColumnMenu = React.forwardRef( +const GridColumnMenu = forwardRef( function GridColumnMenu(props, ref) { return ( ({ minWidth: 248, })); -const GridColumnMenuContainer = React.forwardRef( +const GridColumnMenuContainer = forwardRef( function GridColumnMenuContainer(props, ref) { const { hideMenu, colDef, id, labelledby, className, children, open, ...other } = props; @@ -31,12 +32,12 @@ const GridColumnMenuContainer = React.forwardRef {children} diff --git a/packages/x-data-grid/src/components/panel/GridPanel.tsx b/packages/x-data-grid/src/components/panel/GridPanel.tsx index ed3473b277343..eddf057831c3f 100644 --- a/packages/x-data-grid/src/components/panel/GridPanel.tsx +++ b/packages/x-data-grid/src/components/panel/GridPanel.tsx @@ -6,6 +6,7 @@ import { unstable_generateUtilityClasses as generateUtilityClasses } from '@mui/ import ClickAwayListener from '@mui/material/ClickAwayListener'; import Paper from '@mui/material/Paper'; import Popper from '@mui/material/Popper'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -54,7 +55,7 @@ const GridPaperRoot = styled(Paper, { overflow: 'auto', })); -const GridPanel = React.forwardRef((props, ref) => { +const GridPanel = forwardRef((props, ref) => { const { children, className, classes: classesProp, ...other } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); @@ -116,13 +117,13 @@ const GridPanel = React.forwardRef((props, ref) return ( ( +const GridPanelWrapper = forwardRef( function GridPanelWrapper(props, ref) { const { className, slotProps = {}, ...other } = props; const rootProps = useGridRootProps(); @@ -53,11 +54,11 @@ const GridPanelWrapper = React.forwardRef return ( ); diff --git a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx index bb385fd4a3166..217d7192f65c9 100644 --- a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx +++ b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx @@ -8,6 +8,7 @@ import { import { SelectChangeEvent } from '@mui/material/Select'; import { styled } from '@mui/material/styles'; import clsx from 'clsx'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { gridFilterableColumnDefinitionsSelector, gridColumnLookupSelector, @@ -200,7 +201,7 @@ const getColumnLabel = (col: GridColDef) => col.headerName || col.field; const collator = new Intl.Collator(); -const GridFilterForm = React.forwardRef( +const GridFilterForm = forwardRef( function GridFilterForm(props, ref) { const { item, @@ -415,11 +416,11 @@ const GridFilterForm = React.forwardRef( return ( ({ id: Math.round(Math.random() * 1e5), }); -const GridFilterPanel = React.forwardRef( +const GridFilterPanel = forwardRef( function GridFilterPanel(props, ref) { const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); @@ -234,7 +235,7 @@ const GridFilterPanel = React.forwardRef( }, [validFilters.length]); return ( - + {readOnlyFilters.map((item, index) => ( ( - function GridToolbar(props, ref) { - // TODO v7: think about where export option should be passed. - // from slotProps={{ toolbarExport: { ...exportOption } }} seems to be more appropriate - const { - className, - csvOptions, - printOptions, - excelOptions, - showQuickFilter = false, - quickFilterProps = {}, - ...other - } = props as typeof props & { excelOptions: any }; - const rootProps = useGridRootProps(); +const GridToolbar = forwardRef(function GridToolbar(props, ref) { + // TODO v7: think about where export option should be passed. + // from slotProps={{ toolbarExport: { ...exportOption } }} seems to be more appropriate + const { + className, + csvOptions, + printOptions, + excelOptions, + showQuickFilter = false, + quickFilterProps = {}, + ...other + } = props as typeof props & { excelOptions: any }; + const rootProps = useGridRootProps(); - if ( - rootProps.disableColumnFilter && - rootProps.disableColumnSelector && - rootProps.disableDensitySelector && - !showQuickFilter - ) { - return null; - } + if ( + rootProps.disableColumnFilter && + rootProps.disableColumnSelector && + rootProps.disableDensitySelector && + !showQuickFilter + ) { + return null; + } - return ( - - - - - -
- {showQuickFilter && } - - ); - }, -); + return ( + + + + + +
+ {showQuickFilter && } + + ); +}); GridToolbar.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarColumnsButton.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarColumnsButton.tsx index bd93594271972..fe4b139e6659e 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarColumnsButton.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarColumnsButton.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import useId from '@mui/utils/useId'; import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { gridPreferencePanelStateSelector } from '../../hooks/features/preferencesPanel/gridPreferencePanelSelector'; import { GridPreferencePanelsValue } from '../../hooks/features/preferencesPanel/gridPreferencePanelsValue'; @@ -17,7 +18,7 @@ interface GridToolbarColumnsButtonProps { slotProps?: { button?: Partial; tooltip?: Partial }; } -const GridToolbarColumnsButton = React.forwardRef( +const GridToolbarColumnsButton = forwardRef( function GridToolbarColumnsButton(props, ref) { const { slotProps = {} } = props; const buttonProps = slotProps.button || {}; @@ -61,7 +62,6 @@ const GridToolbarColumnsButton = React.forwardRef {apiRef.current.getLocaleText('toolbarColumns')} diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx index 6437188589329..83d7eb1b49419 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { unstable_useId as useId, unstable_useForkRef as useForkRef } from '@mui/utils'; import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { gridDensitySelector } from '../../hooks/features/density/densitySelector'; import { GridDensity } from '../../models/gridDensity'; import { isHideMenuKey } from '../../utils/keyboardUtils'; @@ -21,132 +22,131 @@ interface GridToolbarDensitySelectorProps { slotProps?: { button?: Partial; tooltip?: Partial }; } -const GridToolbarDensitySelector = React.forwardRef< - HTMLButtonElement, - GridToolbarDensitySelectorProps ->(function GridToolbarDensitySelector(props, ref) { - const { slotProps = {} } = props; - const buttonProps = slotProps.button || {}; - const tooltipProps = slotProps.tooltip || {}; - const apiRef = useGridApiContext(); - const rootProps = useGridRootProps(); - const density = useGridSelector(apiRef, gridDensitySelector); - const densityButtonId = useId(); - const densityMenuId = useId(); +const GridToolbarDensitySelector = forwardRef( + function GridToolbarDensitySelector(props, ref) { + const { slotProps = {} } = props; + const buttonProps = slotProps.button || {}; + const tooltipProps = slotProps.tooltip || {}; + const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); + const density = useGridSelector(apiRef, gridDensitySelector); + const densityButtonId = useId(); + const densityMenuId = useId(); - const [open, setOpen] = React.useState(false); - const buttonRef = React.useRef(null); - const handleRef = useForkRef(ref, buttonRef); + const [open, setOpen] = React.useState(false); + const buttonRef = React.useRef(null); + const handleRef = useForkRef(ref, buttonRef); - const densityOptions: GridDensityOption[] = [ - { - icon: , - label: apiRef.current.getLocaleText('toolbarDensityCompact'), - value: 'compact', - }, - { - icon: , - label: apiRef.current.getLocaleText('toolbarDensityStandard'), - value: 'standard', - }, - { - icon: , - label: apiRef.current.getLocaleText('toolbarDensityComfortable'), - value: 'comfortable', - }, - ]; + const densityOptions: GridDensityOption[] = [ + { + icon: , + label: apiRef.current.getLocaleText('toolbarDensityCompact'), + value: 'compact', + }, + { + icon: , + label: apiRef.current.getLocaleText('toolbarDensityStandard'), + value: 'standard', + }, + { + icon: , + label: apiRef.current.getLocaleText('toolbarDensityComfortable'), + value: 'comfortable', + }, + ]; - const startIcon = React.useMemo(() => { - switch (density) { - case 'compact': - return ; - case 'comfortable': - return ; - default: - return ; - } - }, [density, rootProps]); - - const handleDensitySelectorOpen = (event: React.MouseEvent) => { - setOpen((prevOpen) => !prevOpen); - buttonProps.onClick?.(event); - }; - const handleDensitySelectorClose = () => { - setOpen(false); - }; - const handleDensityUpdate = (newDensity: GridDensity) => { - apiRef.current.setDensity(newDensity); - setOpen(false); - }; + const startIcon = React.useMemo(() => { + switch (density) { + case 'compact': + return ; + case 'comfortable': + return ; + default: + return ; + } + }, [density, rootProps]); - const handleListKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Tab') { - event.preventDefault(); - } - if (isHideMenuKey(event.key)) { + const handleDensitySelectorOpen = (event: React.MouseEvent) => { + setOpen((prevOpen) => !prevOpen); + buttonProps.onClick?.(event); + }; + const handleDensitySelectorClose = () => { setOpen(false); - } - }; + }; + const handleDensityUpdate = (newDensity: GridDensity) => { + apiRef.current.setDensity(newDensity); + setOpen(false); + }; - // Disable the button if the corresponding is disabled - if (rootProps.disableDensitySelector) { - return null; - } + const handleListKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Tab') { + event.preventDefault(); + } + if (isHideMenuKey(event.key)) { + setOpen(false); + } + }; - const densityElements = densityOptions.map((option, index) => ( - handleDensityUpdate(option.value)} - selected={option.value === density} - iconStart={option.icon} - > - {option.label} - - )); + // Disable the button if the corresponding is disabled + if (rootProps.disableDensitySelector) { + return null; + } - return ( - - ((option, index) => ( + handleDensityUpdate(option.value)} + selected={option.value === density} + iconStart={option.icon} > - + )); + + return ( + + - {apiRef.current.getLocaleText('toolbarDensity')} - - - - + {apiRef.current.getLocaleText('toolbarDensity')} + + + - {densityElements} - - - - ); -}); + + {densityElements} + + + + ); + }, +); GridToolbarDensitySelector.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx index 4bd3286015158..078fb16c8d314 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridCsvExportOptions, GridPrintExportOptions } from '../../models/gridExport'; @@ -115,7 +116,7 @@ GridPrintExportMenuItem.propTypes = { }), } as any; -const GridToolbarExport = React.forwardRef( +const GridToolbarExport = forwardRef( function GridToolbarExport(props, ref) { const { csvOptions = {}, diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarExportContainer.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarExportContainer.tsx index 0a06d9e234ec0..18a11224870e5 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarExportContainer.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarExportContainer.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { unstable_useId as useId, unstable_useForkRef as useForkRef } from '@mui/utils'; import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { isHideMenuKey } from '../../utils/keyboardUtils'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridMenu } from '../menu/GridMenu'; @@ -17,7 +18,7 @@ interface GridToolbarExportContainerProps { slotProps?: { button?: Partial; tooltip?: Partial }; } -const GridToolbarExportContainer = React.forwardRef< +const GridToolbarExportContainer = forwardRef< HTMLButtonElement, React.PropsWithChildren >(function GridToolbarExportContainer(props, ref) { @@ -63,7 +64,6 @@ const GridToolbarExportContainer = React.forwardRef< {...tooltipProps} > } aria-expanded={open} @@ -74,6 +74,7 @@ const GridToolbarExportContainer = React.forwardRef< onClick={handleMenuOpen} {...rootProps.slotProps?.baseButton} {...buttonProps} + ref={handleRef} > {apiRef.current.getLocaleText('toolbarExport')} diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx index 1f6f55aa594e5..46ba3a47cc4f1 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx @@ -8,6 +8,7 @@ import { } from '@mui/utils'; import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { BadgeProps } from '../../models/gridBaseSlots'; import { gridColumnLookupSelector } from '../../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; @@ -55,7 +56,7 @@ export interface GridToolbarFilterButtonProps { }; } -const GridToolbarFilterButton = React.forwardRef( +const GridToolbarFilterButton = forwardRef( function GridToolbarFilterButton(props, ref) { const { slotProps = {} } = props; const buttonProps = slotProps.button || {}; @@ -142,7 +143,6 @@ const GridToolbarFilterButton = React.forwardRef {apiRef.current.getLocaleText('toolbarFilters')} diff --git a/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx b/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx index fce2b03a4c2ec..812420b1dc0b3 100644 --- a/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { styled } from '@mui/system'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridConfiguration } from '../../hooks/utils/useGridConfiguration'; @@ -25,7 +26,7 @@ const Element = styled('div', { flexDirection: 'column', }); -export const GridMainContainer = React.forwardRef< +export const GridMainContainer = forwardRef< HTMLDivElement, React.PropsWithChildren<{ className: string; @@ -37,12 +38,12 @@ export const GridMainContainer = React.forwardRef< return ( {props.children} diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx index ba3534bfbc79f..b80f77d6d34db 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx @@ -5,6 +5,7 @@ import { unstable_useForkRef as useForkRef, unstable_useEventCallback as useEventCallback, } from '@mui/utils'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useOnMount } from '../../hooks/utils/useOnMount'; import { useGridPrivateApiContext } from '../../hooks/utils/useGridPrivateApiContext'; import { gridDimensionsSelector, useGridSelector } from '../../hooks'; @@ -66,7 +67,7 @@ const ScrollbarHorizontal = styled(Scrollbar)({ bottom: '0px', }); -const GridVirtualScrollbar = React.forwardRef( +const GridVirtualScrollbar = forwardRef( function GridVirtualScrollbar(props, ref) { const apiRef = useGridPrivateApiContext(); const rootProps = useGridRootProps(); diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerContent.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerContent.tsx index 90709353bf2b7..935a508817a42 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerContent.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerContent.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import clsx from 'clsx'; import { styled, SxProps, Theme } from '@mui/system'; import composeClasses from '@mui/utils/composeClasses'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { DataGridProcessedProps } from '../../models/props/DataGridProps'; @@ -24,7 +25,7 @@ const VirtualScrollerContentRoot = styled('div', { overridesResolver: (props, styles) => styles.virtualScrollerContent, })<{ ownerState: OwnerState }>({}); -const GridVirtualScrollerContent = React.forwardRef< +const GridVirtualScrollerContent = forwardRef< HTMLDivElement, React.HTMLAttributes & { sx?: SxProps } >(function GridVirtualScrollerContent(props, ref) { @@ -34,10 +35,10 @@ const GridVirtualScrollerContent = React.forwardRef< return ( ); }); diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerRenderZone.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerRenderZone.tsx index 4ecd09eaf4f82..fd8b70c85035a 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerRenderZone.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerRenderZone.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import clsx from 'clsx'; import { styled, SxProps, Theme } from '@mui/system'; import composeClasses from '@mui/utils/composeClasses'; +import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { gridRowsMetaSelector } from '../../hooks/features/rows'; @@ -32,7 +33,7 @@ const VirtualScrollerRenderZoneRoot = styled('div', { flexDirection: 'column', }); -const GridVirtualScrollerRenderZone = React.forwardRef< +const GridVirtualScrollerRenderZone = forwardRef< HTMLDivElement, React.HTMLAttributes & { sx?: SxProps } >(function GridVirtualScrollerRenderZone(props, ref) { @@ -48,13 +49,13 @@ const GridVirtualScrollerRenderZone = React.forwardRef< return ( ); }); diff --git a/packages/x-internals/src/forwardRef/forwardRef.tsx b/packages/x-internals/src/forwardRef/forwardRef.tsx new file mode 100644 index 0000000000000..f47f9755a26c8 --- /dev/null +++ b/packages/x-internals/src/forwardRef/forwardRef.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import reactMajor from '../reactMajor'; + +// Compatibility shim that ensures stable props object for forwardRef components +// Fixes https://github.com/facebook/react/issues/31613 +// We ensure that the ref is always present in the props object (even if that's not the case for older versions of React) to avoid the footgun of spreading props over the ref in the newer versions of React. +// Footgun: will break past React 19, but the types will now warn us that we should use instead. +export const forwardRef = ( + render: React.ForwardRefRenderFunction }>, +) => { + if (reactMajor >= 19) { + const Component = (props: any) => render(props, props.ref ?? null); + Component.displayName = render.displayName ?? render.name; + return Component as React.ForwardRefExoticComponent

; + } + return React.forwardRef(render as React.ForwardRefRenderFunction>); +}; diff --git a/packages/x-internals/src/forwardRef/index.ts b/packages/x-internals/src/forwardRef/index.ts new file mode 100644 index 0000000000000..6b0e2aa587d46 --- /dev/null +++ b/packages/x-internals/src/forwardRef/index.ts @@ -0,0 +1 @@ +export * from './forwardRef';