diff --git a/CHANGELOG.md b/CHANGELOG.md index 283860ec..d4c6742c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to `dash-ag-grid` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). Links "DE#nnn" prior to version 2.0 point to the Dash Enterprise closed-source Dash AG Grid repo +## [UNRELEASED] + +### Fixed + +- [#391](https://github.com/plotly/dash-ag-grid/pull/391) Removed use of defaultProps for improved compatibility with React functional components. Default prop values are now set directly in the component using destructuring or fallback logic. + ## [32.3.0] - 2025-07-23 ### Changed diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js index 829f23b7..3535ebd2 100644 --- a/src/lib/components/AgGrid.react.js +++ b/src/lib/components/AgGrid.react.js @@ -56,24 +56,21 @@ function DashAgGrid(props) { DashAgGrid.dashRenderType = true; -DashAgGrid.defaultProps = { - className: '', - resetColumnState: false, - exportDataAsCsv: false, - selectAll: false, - deselectAll: false, - enableEnterpriseModules: false, - updateColumnState: false, +/** Apply persistence settings based on React version for dash 2 compatibility */ +const REACT_VERSION_DASH2_COMPAT = 18.3; +const persistence = { persisted_props: ['selectedRows'], persistence_type: 'local', - suppressDragLeaveHidesColumns: true, - dangerously_allow_code: false, - rowModelType: 'clientSide', - dashGridOptions: {}, - filterModel: {}, - paginationGoTo: null, - selectedRows: [], }; +if ( + parseFloat(React.version.substring(0, React.version.lastIndexOf('.'))) < + REACT_VERSION_DASH2_COMPAT +) { + DashAgGrid.defaultProps = persistence; +} else { + DashAgGrid.dashPersistence = persistence; +} + DashAgGrid.propTypes = { /******************************** * DASH PROPS @@ -755,7 +752,7 @@ DashAgGrid.propTypes = { }; export const propTypes = DashAgGrid.propTypes; -export const defaultProps = DashAgGrid.defaultProps; +// export const defaultProps = DashAgGrid.defaultProps; export default DashAgGrid; diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js index c3a4cafc..a9479192 100644 --- a/src/lib/fragments/AgGrid.react.js +++ b/src/lib/fragments/AgGrid.react.js @@ -15,11 +15,7 @@ import { assoc, assocPath, } from 'ramda'; -import { - propTypes as _propTypes, - defaultProps as _defaultProps, - apiGetters, -} from '../components/AgGrid.react'; +import {propTypes as _propTypes, apiGetters} from '../components/AgGrid.react'; import { COLUMN_DANGEROUS_FUNCTIONS, COLUMN_MAYBE_FUNCTIONS, @@ -128,7 +124,11 @@ function usePrevious(value) { return ref.current; } -export function DashAgGrid(props) { +export function DashAgGrid({ + className = '', + suppressDragLeaveHidesColumns = true, + ...props +}) { const active = useRef(true); // const customSetProps = props.setProps; @@ -684,7 +684,7 @@ export function DashAgGrid(props) { convertAllPropsRef.current = convertAllProps; const virtualRowData = useCallback(() => { - const {rowModelType} = props; + const {rowModelType = 'clientSide'} = props; const virtualRowData = []; if (rowModelType === 'clientSide' && gridApi) { gridApi.forEachNodeAfterFilterAndSort((node) => { @@ -697,7 +697,7 @@ export function DashAgGrid(props) { }, [props.rowModelType, gridApi]); const onFilterChanged = useCallback(() => { - const {rowModelType} = props; + const {rowModelType = 'clientSide'} = props; if (!gridApi) { return; } @@ -735,7 +735,7 @@ export function DashAgGrid(props) { }, [props.rowData, virtualRowData, getRowData, customSetProps]); const onSortChanged = useCallback(() => { - const {rowModelType} = props; + const {rowModelType = 'clientSide'} = props; const propsToSet = {}; if (rowModelType === 'clientSide') { propsToSet.virtualRowData = virtualRowData(); @@ -750,12 +750,17 @@ export function DashAgGrid(props) { const onRowDataUpdated = useCallback(() => { // Handles preserving existing selections when rowData is updated in a callback - const {selectedRows, rowData, rowModelType, filterModel} = props; + const { + selectedRows = [], + rowData, + rowModelType = 'clientSide', + filterModel = {}, + } = props; if (gridApi && !gridApi?.isDestroyed()) { dataUpdates.current = true; pauseSelections.current = true; - setSelection(selectedRows); + setSelection(selectedRows ?? []); if (rowData && rowModelType === 'clientSide') { const virtualRowDataResult = virtualRowData(); @@ -807,7 +812,7 @@ export function DashAgGrid(props) { setTimeout(() => { if (!pauseSelections.current) { const selectedRows = gridApi.getSelectedRows(); - if (!equals(selectedRows, props.selectedRows)) { + if (!equals(selectedRows, props.selectedRows ?? [])) { // Flag that the selection event was fired selectionEventFired.current = true; customSetProps({selectedRows}); @@ -843,7 +848,7 @@ export function DashAgGrid(props) { if (data.async === false) { gridApiParam.applyTransaction(data); if (selectedRows) { - setSelection(selectedRows); + setSelection(selectedRows ?? []); } } else { gridApiParam.applyTransactionAsync(data); @@ -856,7 +861,7 @@ export function DashAgGrid(props) { (params) => { // Applying Infinite Row Model // see: https://www.ag-grid.com/javascript-grid/infinite-scrolling/ - const {rowModelType, eventListeners} = props; + const {rowModelType = 'clientSide', eventListeners} = props; if (rowModelType === 'infinite') { params.api.setGridOption('datasource', getDatasource()); @@ -1259,7 +1264,7 @@ export function DashAgGrid(props) { ); const onAsyncTransactionsFlushed = useCallback(() => { - const {selectedRows} = props; + const {selectedRows = []} = props; if (selectedRows) { setSelection(selectedRows); } @@ -1288,8 +1293,8 @@ export function DashAgGrid(props) { // Apply selections if (gridApi) { const selectedRows = gridApi.getSelectedRows(); - if (!equals(selectedRows, props.selectedRows)) { - setSelection(props.selectedRows); + if (!equals(selectedRows, props.selectedRows ?? [])) { + setSelection(props.selectedRows ?? []); } } }, [props.selectedRows, gridApi]); @@ -1339,8 +1344,12 @@ export function DashAgGrid(props) { // Handle gridApi initialization - filter model application useEffect(() => { - if (gridApi && gridApi !== prevGridApi && !isEmpty(props.filterModel)) { - gridApi.setFilterModel(props.filterModel); + if ( + gridApi && + gridApi !== prevGridApi && + !isEmpty(props.filterModel ?? {}) + ) { + gridApi.setFilterModel(props.filterModel ?? {}); } }, [gridApi, prevGridApi, props.filterModel]); @@ -1433,10 +1442,10 @@ export function DashAgGrid(props) { if ( gridApi && gridApi === prevGridApi && - props.filterModel && - gridApi.getFilterModel() !== props.filterModel + (props.filterModel ?? {}) && + gridApi.getFilterModel() !== (props.filterModel ?? {}) ) { - gridApi.setFilterModel(props.filterModel); + gridApi.setFilterModel(props.filterModel ?? {}); } }, [props.filterModel, gridApi, prevGridApi]); @@ -1530,7 +1539,7 @@ export function DashAgGrid(props) { setColumnState, ]); - const {id, style, className, dashGridOptions, ...restProps} = props; + const {id, style, dashGridOptions = {}, ...restProps} = props; const passingProps = pick(PASSTHRU_PROPS, restProps); const convertedProps = convertAllProps( omit(NO_CONVERT_PROPS, {...dashGridOptions, ...restProps}) @@ -1612,16 +1621,17 @@ export function DashAgGrid(props) { components={components} {...passingProps} {...convertedProps} + suppressDragLeaveHidesColumns={suppressDragLeaveHidesColumns} > ); } -DashAgGrid.defaultProps = _defaultProps; +// DashAgGrid.defaultProps = _defaultProps; DashAgGrid.propTypes = {parentState: PropTypes.any, ..._propTypes}; export const propTypes = DashAgGrid.propTypes; -export const defaultProps = DashAgGrid.defaultProps; +// export const defaultProps = DashAgGrid.defaultProps; var dagfuncs = (window.dash_ag_grid = window.dash_ag_grid || {}); dagfuncs.useGridFilter = useGridFilter;