From 7b440c78abf0f5a3cbe10a3bef8e1b2b659454bb Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:00:07 -0400 Subject: [PATCH 01/11] minor tweak to column state chain for updates --- src/lib/fragments/AgGrid.react.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js index 7250339..c998c9a 100644 --- a/src/lib/fragments/AgGrid.react.js +++ b/src/lib/fragments/AgGrid.react.js @@ -1342,10 +1342,10 @@ export function DashAgGrid(props) { // Handle gridApi initialization - column state application useEffect(() => { - if (gridApi && gridApi !== prevGridApi && props.columnState) { + if (gridApi && gridApi !== prevGridApi && columnState_push) { setColumnState(); } - }, [gridApi, prevGridApi, props.columnState, setColumnState]); + }, [gridApi, prevGridApi, columnState_push]); // Handle gridApi initialization - finalization useEffect(() => { @@ -1378,7 +1378,7 @@ export function DashAgGrid(props) { setColumnState_push(true); } } - }, [props.columnState, props.loading_state, columnState_push]); + }, [props.columnState, props.loading_state]); // Handle ID changes useEffect(() => { From bd8315981d3874c8042f3417138e22599cd981ea Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:22:10 -0400 Subject: [PATCH 02/11] adding test for the max call stack --- tests/test_column_state.py | 206 ++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 2 deletions(-) diff --git a/tests/test_column_state.py b/tests/test_column_state.py index 5b573b0..2d1b718 100644 --- a/tests/test_column_state.py +++ b/tests/test_column_state.py @@ -1,4 +1,4 @@ -from dash import Dash, html, Output, Input, no_update, State, ctx +from dash import Dash, html, Output, Input, no_update, State, ctx, Patch import dash_ag_grid as dag import plotly.express as px import json @@ -315,4 +315,206 @@ def make_grid(n): for x in range(10): dash_duo.find_element("#add-grid").click() time.sleep(2) # pausing to emulate separation because user inputs - assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == [] \ No newline at end of file + assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == [] + +def test_cs003_column_state(dash_duo): + data = [ + { + "localTime": "5:00am", + "a": 0.231, + "b": 0.523, + "c": 0.423, + "d": 0.527, + }, + { + "localTime": "5:15am", + "a": 0.423, + "b": 0.452, + "c": 0.523, + "d": 0.543, + }, + { + "localTime": "5:30am", + "a": 0.537, + "b": 0.246, + "c": 0.426, + "d": 0.421, + }, + { + "localTime": "5:45am", + "a": 0.893, + "b": 0.083, + "c": 0.532, + "d": 0.983, + }, + { + "localTime": "6:00am", + "a": 0.231, + "b": 0.523, + "c": 0.423, + "d": 0.527, + }, + { + "localTime": "6:15am", + "a": 0.423, + "b": 0.452, + "c": 0.523, + "d": 0.543, + }, + { + "localTime": "6:30am", + "a": 0.537, + "b": 0.246, + "c": 0.426, + "d": 0.421, + }, + { + "localTime": "6:45am", + "a": 0.893, + "b": 0.083, + "c": 0.532, + "d": 0.983, + }, + { + "localTime": "7:00am", + "a": 0.231, + "b": 0.523, + "c": 0.423, + "d": 0.527, + }, + { + "localTime": "7:15am", + "a": 0.423, + "b": 0.452, + "c": 0.523, + "d": 0.543, + }, + { + "localTime": "7:30am", + "a": 0.537, + "b": 0.246, + "c": 0.426, + "d": 0.421, + }, + { + "localTime": "7:45am", + "a": 0.893, + "b": 0.083, + "c": 0.532, + "d": 0.983, + }, + { + "localTime": "8:00am", + "a": 0.231, + "b": 0.523, + "c": 0.423, + "d": 0.527, + }, + { + "localTime": "8:15am", + "a": 0.423, + "b": 0.452, + "c": 0.523, + "d": 0.543, + }, + { + "localTime": "8:30am", + "a": 0.537, + "b": 0.246, + "c": 0.426, + "d": 0.421, + }, + { + "localTime": "8:45am", + "a": 0.893, + "b": 0.083, + "c": 0.532, + "d": 0.983, + }, + { + "localTime": "8:00am", + "a": 0.231, + "b": 0.523, + "c": 0.423, + "d": 0.527, + }, + { + "localTime": "8:15am", + "a": 0.423, + "b": 0.452, + "c": 0.523, + "d": 0.543, + }, + { + "localTime": "8:30am", + "a": 0.537, + "b": 0.246, + "c": 0.426, + "d": 0.421, + }, + { + "localTime": "8:45am", + "a": 0.893, + "b": 0.083, + "c": 0.532, + "d": 0.983, + }, + ] + + columnDefs = [ + {"field": "localTime"}, + {"field": "a"}, + {"field": "b"}, + {"field": "c"}, + {"field": "d"}, + ] + + app = Dash(__name__) + + app.layout = html.Div( + [ + html.Div( + [ + html.Button( + "Remove Column", id="remove-column", n_clicks=0 + ), + html.Div( + id='grid-holder', + children=[ + dag.AgGrid( + id=f"grid", + columnDefs=columnDefs, + rowData=data, + columnSize='autoSize' + ) + ] + ) + ], + ), + ] + ) + + @app.callback( + Output('grid', 'columnDefs'), + Input('remove-column', 'n_clicks'), + ) + def remove_column(n): + if n: + cols = Patch() + if n < 3: + del cols[0] + return cols + return no_update + + dash_duo.start_server(app, + debug=True, + use_reloader=False, + use_debugger=True, + dev_tools_hot_reload=False, + dev_tools_props_check=True, + dev_tools_disable_version_check=True,) + + for x in range(3): + dash_duo.find_element("#remove-column").click() + time.sleep(2) # pausing to emulate separation because user inputs + assert list(filter(lambda i: i.get("level") != "ERROR", dash_duo.get_logs())) == [] \ No newline at end of file From 4386b46343568929869f4ca6fdb6fe33b3b05ded Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:25:27 -0400 Subject: [PATCH 03/11] running black on the new test --- tests/test_column_state.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_column_state.py b/tests/test_column_state.py index 2d1b718..3d51627 100644 --- a/tests/test_column_state.py +++ b/tests/test_column_state.py @@ -475,28 +475,26 @@ def test_cs003_column_state(dash_duo): [ html.Div( [ - html.Button( - "Remove Column", id="remove-column", n_clicks=0 - ), + html.Button("Remove Column", id="remove-column", n_clicks=0), html.Div( - id='grid-holder', + id="grid-holder", children=[ dag.AgGrid( id=f"grid", columnDefs=columnDefs, rowData=data, - columnSize='autoSize' + columnSize="autoSize", ) - ] - ) + ], + ), ], ), ] ) @app.callback( - Output('grid', 'columnDefs'), - Input('remove-column', 'n_clicks'), + Output("grid", "columnDefs"), + Input("remove-column", "n_clicks"), ) def remove_column(n): if n: @@ -506,15 +504,17 @@ def remove_column(n): return cols return no_update - dash_duo.start_server(app, - debug=True, - use_reloader=False, + dash_duo.start_server( + app, + debug=True, + use_reloader=False, use_debugger=True, dev_tools_hot_reload=False, dev_tools_props_check=True, - dev_tools_disable_version_check=True,) + dev_tools_disable_version_check=True, + ) for x in range(3): dash_duo.find_element("#remove-column").click() time.sleep(2) # pausing to emulate separation because user inputs - assert list(filter(lambda i: i.get("level") != "ERROR", dash_duo.get_logs())) == [] \ No newline at end of file + assert list(filter(lambda i: i.get("level") != "ERROR", dash_duo.get_logs())) == [] From cef0a60707f5bc1e8276eca814a77030ee644575 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 25 Jul 2025 21:02:08 -0400 Subject: [PATCH 04/11] test of removal of default props --- src/lib/components/AgGrid.react.js | 51 ++++++++++++++++++------------ src/lib/fragments/AgGrid.react.js | 2 -- src/lib/utils/propCategories.js | 3 ++ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js index 5813dd3..7aa711f 100644 --- a/src/lib/components/AgGrid.react.js +++ b/src/lib/components/AgGrid.react.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import LazyLoader from '../LazyLoader'; import React, {lazy, Suspense, useState, useCallback, useEffect} from 'react'; +import { pick } from 'ramda'; const RealAgGrid = lazy(LazyLoader.agGrid); const RealAgGridEnterprise = lazy(LazyLoader.agGridEnterprise); @@ -9,6 +10,25 @@ function getGrid(enable) { return enable ? RealAgGridEnterprise : RealAgGrid; } +export const defaultProps = { + className: 'ag-theme-alpine', + resetColumnState: false, + exportDataAsCsv: false, + selectAll: false, + deselectAll: false, + enableEnterpriseModules: false, + updateColumnState: false, + persisted_props: ['selectedRows'], + persistence_type: 'local', + suppressDragLeaveHidesColumns: true, + dangerously_allow_code: false, + rowModelType: 'clientSide', + dashGridOptions: {}, + filterModel: {}, + paginationGoTo: null, + selectedRows: [], +}; + /** * Dash interface to AG Grid, a powerful tabular data component. */ @@ -45,31 +65,23 @@ function DashAgGrid(props) { return ( - + ); } +const REACT_VERSION_DASH2_COMPAT = 18.3; +if ( + parseFloat(React.version.substring(0, React.version.lastIndexOf('.'))) < + REACT_VERSION_DASH2_COMPAT +) { + DashAgGrid.defaultProps = defaultProps; +} else { + DashAgGrid.dashPersistence = pick(['persisted_props', 'persistence_type'],defaultProps); +} + DashAgGrid.dashRenderType = true; -DashAgGrid.defaultProps = { - className: 'ag-theme-alpine', - resetColumnState: false, - exportDataAsCsv: false, - selectAll: false, - deselectAll: false, - enableEnterpriseModules: false, - updateColumnState: false, - persisted_props: ['selectedRows'], - persistence_type: 'local', - suppressDragLeaveHidesColumns: true, - dangerously_allow_code: false, - rowModelType: 'clientSide', - dashGridOptions: {}, - filterModel: {}, - paginationGoTo: null, - selectedRows: [], -}; DashAgGrid.propTypes = { /******************************** * DASH PROPS @@ -751,7 +763,6 @@ DashAgGrid.propTypes = { }; export const propTypes = DashAgGrid.propTypes; -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 c998c9a..bd482cd 100644 --- a/src/lib/fragments/AgGrid.react.js +++ b/src/lib/fragments/AgGrid.react.js @@ -1605,11 +1605,9 @@ export function DashAgGrid(props) { ); } -DashAgGrid.defaultProps = _defaultProps; DashAgGrid.propTypes = {parentState: PropTypes.any, ..._propTypes}; export const propTypes = DashAgGrid.propTypes; -export const defaultProps = DashAgGrid.defaultProps; var dagfuncs = (window.dash_ag_grid = window.dash_ag_grid || {}); dagfuncs.useGridFilter = useGridFilter; diff --git a/src/lib/utils/propCategories.js b/src/lib/utils/propCategories.js index a2f8cf0..253fe6d 100644 --- a/src/lib/utils/propCategories.js +++ b/src/lib/utils/propCategories.js @@ -321,6 +321,8 @@ export const PASSTHRU_PROPS = ['rowData']; */ export const PROPS_NOT_FOR_AG_GRID = [ 'children', + 'dashRenderType', + 'licenseKey', 'setProps', 'loading_state', 'enableEnterpriseModules', @@ -354,6 +356,7 @@ export const PROPS_NOT_FOR_AG_GRID = [ 'scrollTo', 'eventListeners', 'eventData', + 'paginationInfo', ]; /** From f81fa6cf9c7d231733b2abb6af59e6bbe63b8ebb Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:15:36 -0400 Subject: [PATCH 05/11] fixing for lint --- src/lib/fragments/AgGrid.react.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js index bd482cd..6dbb707 100644 --- a/src/lib/fragments/AgGrid.react.js +++ b/src/lib/fragments/AgGrid.react.js @@ -17,7 +17,6 @@ import { } from 'ramda'; import { propTypes as _propTypes, - defaultProps as _defaultProps, apiGetters, } from '../components/AgGrid.react'; import { From 9ecc2465160934489c4017d63c03412c253313c0 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:17:28 -0400 Subject: [PATCH 06/11] fix for lint again --- src/lib/components/AgGrid.react.js | 9 ++++++--- src/lib/fragments/AgGrid.react.js | 5 +---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js index 7aa711f..52c55ca 100644 --- a/src/lib/components/AgGrid.react.js +++ b/src/lib/components/AgGrid.react.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import LazyLoader from '../LazyLoader'; import React, {lazy, Suspense, useState, useCallback, useEffect} from 'react'; -import { pick } from 'ramda'; +import {pick} from 'ramda'; const RealAgGrid = lazy(LazyLoader.agGrid); const RealAgGridEnterprise = lazy(LazyLoader.agGridEnterprise); @@ -72,12 +72,15 @@ function DashAgGrid(props) { const REACT_VERSION_DASH2_COMPAT = 18.3; if ( - parseFloat(React.version.substring(0, React.version.lastIndexOf('.'))) < + parseFloat(React.version.substring(0, React.version.lastIndexOf('.'))) < REACT_VERSION_DASH2_COMPAT ) { DashAgGrid.defaultProps = defaultProps; } else { - DashAgGrid.dashPersistence = pick(['persisted_props', 'persistence_type'],defaultProps); + DashAgGrid.dashPersistence = pick( + ['persisted_props', 'persistence_type'], + defaultProps + ); } DashAgGrid.dashRenderType = true; diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js index 6dbb707..12f0b95 100644 --- a/src/lib/fragments/AgGrid.react.js +++ b/src/lib/fragments/AgGrid.react.js @@ -15,10 +15,7 @@ import { assoc, assocPath, } from 'ramda'; -import { - propTypes as _propTypes, - apiGetters, -} from '../components/AgGrid.react'; +import {propTypes as _propTypes, apiGetters} from '../components/AgGrid.react'; import { COLUMN_DANGEROUS_FUNCTIONS, COLUMN_MAYBE_FUNCTIONS, From 46603dd149805dffb73fb989342c7d2fe121a1b2 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Tue, 16 Sep 2025 17:33:55 -0600 Subject: [PATCH 07/11] Fix magic number used in test --- tests/test_column_state.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_column_state.py b/tests/test_column_state.py index 3d51627..2b5b45c 100644 --- a/tests/test_column_state.py +++ b/tests/test_column_state.py @@ -84,7 +84,7 @@ alt_colState = [ { "colId": "price", - "width": 198, + "width": 200, "hide": False, "pinned": None, "sort": "asc", @@ -239,7 +239,7 @@ def loadState(n): dash_duo.find_element("#get-column-state-button").click() testState = colState.copy() - testState[1]["width"] = 198 + testState[1]["width"] = 200 until( lambda: json.dumps(testState) in dash_duo.find_element("#reset-column-state-grid-pre").text, From c02cd7415af6e9bdded6482bf2ecc62c868b9c03 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Wed, 17 Sep 2025 09:03:32 -0600 Subject: [PATCH 08/11] v32.3.2 --- CHANGELOG.md | 5 +++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dde1f9a..8ea4fe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ 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 +## [32.3.2] - 2025-09-17 + +### Fixed +- [#403](https://github.com/plotly/dash-ag-grid/issues/403) fix "Maximum update depth exceeded" error + ## [32.3.1] - 2025-08-05 ### Fixed diff --git a/package-lock.json b/package-lock.json index 94b66d9..accaa33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dash-ag-grid", - "version": "32.3.1", + "version": "32.3.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dash-ag-grid", - "version": "32.3.1", + "version": "32.3.2", "license": "MIT", "dependencies": { "@emotion/react": "^11.11.3", diff --git a/package.json b/package.json index 2eacb36..36dad18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dash-ag-grid", - "version": "32.3.1", + "version": "32.3.2", "description": "Dash wrapper around AG Grid, the best interactive data grid for the web.", "repository": { "type": "git", From 71726c0fbf652f626ce03b87a1ae60f0229332b6 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Wed, 17 Sep 2025 09:35:51 -0600 Subject: [PATCH 09/11] address test flakiness --- tests/test_sizing_buttons.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_sizing_buttons.py b/tests/test_sizing_buttons.py index 9433902..36563a4 100644 --- a/tests/test_sizing_buttons.py +++ b/tests/test_sizing_buttons.py @@ -1,3 +1,4 @@ +import pytest import dash_ag_grid as dag from dash import Dash, html, dcc, Output, Input, no_update, ctx, State import requests @@ -118,6 +119,7 @@ def selected(state, oldState, opts): oldValue = dash_duo.find_element("#columnState").text +@pytest.mark.flaky(max_runs=5, min_passes=1) def test_sb002_sizing_buttons(dash_duo): app = Dash(__name__) @@ -204,6 +206,7 @@ def selected(state): for x in columnDefs: assert x["field"] in oldValue + time.sleep(.5) # allow window size to change for x in ["sizeToFit", "responsiveSizeToFit"]: dash_duo.find_element(f"#{x}").click() if x != "responsiveSizeToFit": @@ -339,7 +342,7 @@ def selected(state, oldState, opts): ) oldValue = dash_duo.find_element("#columnState").text - +@pytest.mark.flaky(max_runs=5, min_passes=1) def test_sb004_sizing_buttons(dash_duo): app = Dash(__name__) @@ -427,6 +430,7 @@ def selected(state): oldValue = dash_duo.find_element("#columnState").text for x in columnDefs: assert x["field"] in oldValue + time.sleep(.5) for x in ["sizeToFit", "responsiveSizeToFit"]: dash_duo.find_element(f"#{x}").click() @@ -448,7 +452,7 @@ def selected(state): assert oldValue == dash_duo.find_element("#columnState").get_attribute( "innerText" ) - time.sleep(.2) # allow window size to change + time.sleep(.5) # allow window size to change dash_duo.find_element(f"#{x}").click() until( lambda: oldValue From b52af23fe0b61cf7a9f80fab4d3f0e3da7802ae6 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Wed, 17 Sep 2025 10:09:56 -0600 Subject: [PATCH 10/11] v33.3.2rc2 --- CHANGELOG.md | 5 +++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4555b..051530c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ 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 +## [32.3.2rc2] - 2025-09-17 + +### Fixed +- [#403](https://github.com/plotly/dash-ag-grid/issues/403) fix "Maximum update depth exceeded" error + ## [32.3.2] - 2025-09-17 ### Fixed diff --git a/package-lock.json b/package-lock.json index fa2aa03..b784e43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dash-ag-grid", - "version": "33.3.2rc1", + "version": "33.3.2rc2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dash-ag-grid", - "version": "33.3.2rc1", + "version": "33.3.2rc2", "license": "MIT", "dependencies": { "@emotion/react": "^11.11.3", diff --git a/package.json b/package.json index c30b436..63ed867 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dash-ag-grid", - "version": "33.3.2rc1", + "version": "33.3.2rc2", "description": "Dash wrapper around AG Grid, the best interactive data grid for the web.", "repository": { "type": "git", From 4ad84509f4e5eb2f6558806a8636fba8a75f575b Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Wed, 17 Sep 2025 14:04:20 -0600 Subject: [PATCH 11/11] fix version in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 051530c..c3ce1b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ 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 -## [32.3.2rc2] - 2025-09-17 +## [33.3.2rc2] - 2025-09-17 ### Fixed - [#403](https://github.com/plotly/dash-ag-grid/issues/403) fix "Maximum update depth exceeded" error