From 7dc8d0f821667605be87933d685e33d6cc56781d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Apr 2026 18:40:19 +0000
Subject: [PATCH 01/11] Initial plan
From 88b15b030f38b01f7a3021d827b9f35d830df849 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Apr 2026 18:47:10 +0000
Subject: [PATCH 02/11] fix: stop retaining rowTransaction payloads in wrapper
state
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/94022509-da2c-4214-a58e-b9c85032e29f
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/components/AgGrid.react.js | 31 ++----------------------------
1 file changed, 2 insertions(+), 29 deletions(-)
diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js
index ce2e1c3a..789180ef 100644
--- a/src/lib/components/AgGrid.react.js
+++ b/src/lib/components/AgGrid.react.js
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import LazyLoader from '../LazyLoader';
-import React, {lazy, Suspense, useState, useCallback, useEffect} from 'react';
+import React, {lazy, Suspense} from 'react';
import {AllCommunityModule, ModuleRegistry} from 'ag-grid-community';
import {pick} from 'ramda';
@@ -37,39 +37,12 @@ export const defaultProps = {
* Dash interface to AG Grid, a powerful tabular data component.
*/
function DashAgGrid(props) {
- const [state, setState] = useState({
- mounted: false,
- rowTransaction: null,
- });
-
- const buildArray = useCallback((arr1, arr2) => {
- if (arr1) {
- if (!arr1.includes(arr2)) {
- return [...arr1, arr2];
- }
- return arr1;
- }
- return [JSON.parse(JSON.stringify(arr2))];
- }, []);
-
- useEffect(() => {
- if (props.rowTransaction && !state.mounted) {
- setState((prevState) => ({
- ...prevState,
- rowTransaction: buildArray(
- prevState.rowTransaction,
- props.rowTransaction
- ),
- }));
- }
- }, [props.rowTransaction, state.mounted, buildArray]);
-
const {enableEnterpriseModules} = props;
const RealComponent = getGrid(enableEnterpriseModules);
return (
-
+
);
}
From 2b2783f85c937f14d2dcd694bda043ffe1dcabb7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 10:28:03 +0000
Subject: [PATCH 03/11] fix: preserve pre-mount rowTransaction buffering
without leaks
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/718878d8-cc62-41f8-a36b-d22d6f657dee
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/components/AgGrid.react.js | 44 ++++++++++++++++++++++++--
src/lib/fragments/AgGrid.react.js | 33 ++++++++++++++++---
tests/test_selection_persistence.py | 49 +++++++++++++++++++++++++++++
3 files changed, 120 insertions(+), 6 deletions(-)
diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js
index 789180ef..51f1b7f2 100644
--- a/src/lib/components/AgGrid.react.js
+++ b/src/lib/components/AgGrid.react.js
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import LazyLoader from '../LazyLoader';
-import React, {lazy, Suspense} from 'react';
+import React, {lazy, Suspense, useState, useCallback, useEffect} from 'react';
import {AllCommunityModule, ModuleRegistry} from 'ag-grid-community';
import {pick} from 'ramda';
@@ -37,12 +37,52 @@ export const defaultProps = {
* Dash interface to AG Grid, a powerful tabular data component.
*/
function DashAgGrid(props) {
+ const [state, setState] = useState({
+ mounted: false,
+ rowTransaction: null,
+ });
+
+ const buildArray = useCallback((arr1, arr2) => {
+ if (arr1) {
+ if (!arr1.includes(arr2)) {
+ return [...arr1, arr2];
+ }
+ return arr1;
+ }
+ return [JSON.parse(JSON.stringify(arr2))];
+ }, []);
+
+ useEffect(() => {
+ if (props.rowTransaction && !state.mounted) {
+ setState((prevState) => ({
+ ...prevState,
+ rowTransaction: buildArray(
+ prevState.rowTransaction,
+ props.rowTransaction
+ ),
+ }));
+ }
+ }, [props.rowTransaction, state.mounted, buildArray]);
+
+ const onJsGridMounted = useCallback(() => {
+ setState((prevState) => ({
+ ...prevState,
+ mounted: true,
+ rowTransaction: null,
+ }));
+ }, []);
+
const {enableEnterpriseModules} = props;
const RealComponent = getGrid(enableEnterpriseModules);
return (
-
+
);
}
diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js
index 6f7dad3a..4c3934a4 100644
--- a/src/lib/fragments/AgGrid.react.js
+++ b/src/lib/fragments/AgGrid.react.js
@@ -273,7 +273,9 @@ export function DashAgGrid(props) {
const [, forceRerender] = useState({});
const [openGroups, setOpenGroups] = useState({});
const [columnState_push, setColumnState_push] = useState(true);
- const [rowTransactionState, setRowTransactionState] = useState(null);
+ const [rowTransactionState, setRowTransactionState] = useState(
+ props.parentState?.rowTransaction || null
+ );
const components = useMemo(
() => ({
@@ -297,6 +299,20 @@ export function DashAgGrid(props) {
const getRowsParams = useRef(null);
const pendingCellValueChanges = useRef(null);
+ useEffect(() => {
+ props.onJsGridMounted?.();
+ }, [props.onJsGridMounted]);
+
+ useEffect(() => {
+ if (
+ !gridApi &&
+ props.parentState?.rowTransaction &&
+ props.parentState.rowTransaction !== rowTransactionState
+ ) {
+ setRowTransactionState(props.parentState.rowTransaction);
+ }
+ }, [props.parentState, rowTransactionState, gridApi]);
+
const onPaginationChanged = useCallback(() => {
if (gridApi && !gridApi?.isDestroyed()) {
customSetProps({
@@ -1244,7 +1260,9 @@ export function DashAgGrid(props) {
const rowTransaction = rowTransactionState;
if (gridApi && !gridApi?.isDestroyed()) {
if (rowTransaction) {
- rowTransaction.forEach(applyRowTransaction);
+ rowTransaction.forEach((transaction) =>
+ applyRowTransaction(transaction)
+ );
setRowTransactionState(null);
}
applyRowTransaction(data);
@@ -1543,7 +1561,10 @@ export function DashAgGrid(props) {
const {id, style, className, dashGridOptions, ...restProps} = props;
const passingProps = pick(PASSTHRU_PROPS, restProps);
const convertedProps = convertAllProps(
- omit(NO_CONVERT_PROPS, {...dashGridOptions, ...restProps})
+ omit([...NO_CONVERT_PROPS, 'parentState', 'onJsGridMounted'], {
+ ...dashGridOptions,
+ ...restProps,
+ })
);
if ('theme' in convertedProps) {
@@ -1627,7 +1648,11 @@ export function DashAgGrid(props) {
);
}
-DashAgGrid.propTypes = {parentState: PropTypes.any, ..._propTypes};
+DashAgGrid.propTypes = {
+ parentState: PropTypes.any,
+ onJsGridMounted: PropTypes.func,
+ ..._propTypes,
+};
export const propTypes = DashAgGrid.propTypes;
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index 645efe99..826c3545 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -139,3 +139,52 @@ def setSelections(n, n1, n2):
dash_duo.wait_for_text_to_equal(
"#selectedRows", '[{"make": "Ford", "model": "Mondeo", "price": 32000}]'
)
+
+
+def test_sp002_row_transaction_before_grid_ready(dash_duo):
+ app = Dash(__name__)
+
+ rowData = [
+ {"id": "Toyota_0", "make": "Toyota", "model": "Celica", "price": 35000},
+ {"id": "Ford_0", "make": "Ford", "model": "Mondeo", "price": 32000},
+ {"id": "Porsche_0", "make": "Porsche", "model": "Boxster", "price": 72000},
+ ]
+
+ app.layout = html.Div(
+ [
+ dcc.Interval(id="tick", interval=1, n_intervals=0, max_intervals=1),
+ dag.AgGrid(
+ id="grid",
+ rowData=rowData,
+ columnDefs=[
+ {"field": "id"},
+ {"field": "make"},
+ {"field": "model"},
+ {"field": "price"},
+ ],
+ defaultColDef={"flex": 1},
+ getRowId="params.data.id",
+ ),
+ ]
+ )
+
+ @app.callback(Output("grid", "rowTransaction"), Input("tick", "n_intervals"))
+ def apply_transaction(n):
+ if not n:
+ return dash.no_update
+ return {
+ "add": [
+ {
+ "id": "Queued_1",
+ "make": "Queued",
+ "model": "Queued",
+ "price": 1,
+ }
+ ]
+ }
+
+ dash_duo.start_server(app)
+
+ grid = utils.Grid(dash_duo, "grid")
+ grid.wait_for_cell_text(0, 0, "Toyota_0")
+ grid.wait_for_cell_text(3, 0, "Queued_1")
From e8c7200990e7a95032efaed0a0f84a6d9e07a5c2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 10:31:03 +0000
Subject: [PATCH 04/11] fix: make pre-mount transaction replay robust
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/718878d8-cc62-41f8-a36b-d22d6f657dee
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/components/AgGrid.react.js | 7 ++-----
src/lib/fragments/AgGrid.react.js | 9 ++++++---
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js
index 51f1b7f2..a1046c0f 100644
--- a/src/lib/components/AgGrid.react.js
+++ b/src/lib/components/AgGrid.react.js
@@ -44,12 +44,9 @@ function DashAgGrid(props) {
const buildArray = useCallback((arr1, arr2) => {
if (arr1) {
- if (!arr1.includes(arr2)) {
- return [...arr1, arr2];
- }
- return arr1;
+ return [...arr1, arr2];
}
- return [JSON.parse(JSON.stringify(arr2))];
+ return [arr2];
}, []);
useEffect(() => {
diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js
index 4c3934a4..9fcac73e 100644
--- a/src/lib/fragments/AgGrid.react.js
+++ b/src/lib/fragments/AgGrid.react.js
@@ -74,6 +74,7 @@ const xssMessage = (context) => {
};
const NO_CONVERT_PROPS = [...PASSTHRU_PROPS, ...PROPS_NOT_FOR_AG_GRID];
+const OMIT_GRID_INTERNAL_PROPS = ['parentState', 'onJsGridMounted'];
const dash_clientside = window.dash_clientside || {};
@@ -298,10 +299,12 @@ export function DashAgGrid(props) {
const getDetailParams = useRef();
const getRowsParams = useRef(null);
const pendingCellValueChanges = useRef(null);
+ const onJsGridMountedRef = useRef(props.onJsGridMounted);
+ onJsGridMountedRef.current = props.onJsGridMounted;
useEffect(() => {
- props.onJsGridMounted?.();
- }, [props.onJsGridMounted]);
+ onJsGridMountedRef.current?.();
+ }, []);
useEffect(() => {
if (
@@ -1561,7 +1564,7 @@ export function DashAgGrid(props) {
const {id, style, className, dashGridOptions, ...restProps} = props;
const passingProps = pick(PASSTHRU_PROPS, restProps);
const convertedProps = convertAllProps(
- omit([...NO_CONVERT_PROPS, 'parentState', 'onJsGridMounted'], {
+ omit([...NO_CONVERT_PROPS, ...OMIT_GRID_INTERNAL_PROPS], {
...dashGridOptions,
...restProps,
})
From 651c9608ee190d20c6d601e8605ac36401f10756 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 10:32:32 +0000
Subject: [PATCH 05/11] test: assert existing rows remain after queued
transaction replay
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/718878d8-cc62-41f8-a36b-d22d6f657dee
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
tests/test_selection_persistence.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index 826c3545..1910a868 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -187,4 +187,6 @@ def apply_transaction(n):
grid = utils.Grid(dash_duo, "grid")
grid.wait_for_cell_text(0, 0, "Toyota_0")
+ grid.wait_for_cell_text(1, 0, "Ford_0")
+ grid.wait_for_cell_text(2, 0, "Porsche_0")
grid.wait_for_cell_text(3, 0, "Queued_1")
From 5e06089d5277371e38d9c2a8b65d907f2c0c2c7d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 11:04:38 +0000
Subject: [PATCH 06/11] style: apply prettier to transaction queue updates
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/4f1937a5-6498-482d-81f2-16935edd8811
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/components/AgGrid.react.js | 4 +++-
src/lib/fragments/AgGrid.react.js | 8 ++++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js
index d30faf13..e73e46a3 100644
--- a/src/lib/components/AgGrid.react.js
+++ b/src/lib/components/AgGrid.react.js
@@ -48,7 +48,9 @@ function DashAgGrid(props) {
}
const nextSerialized = JSON.stringify(arr2);
if (
- arr1.some((transaction) => JSON.stringify(transaction) === nextSerialized)
+ arr1.some(
+ (transaction) => JSON.stringify(transaction) === nextSerialized
+ )
) {
return arr1;
}
diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js
index fdb3bdfa..618aac15 100644
--- a/src/lib/fragments/AgGrid.react.js
+++ b/src/lib/fragments/AgGrid.react.js
@@ -1259,7 +1259,9 @@ export function DashAgGrid(props) {
}
const nextSerialized = JSON.stringify(arr2);
if (
- arr1.some((transaction) => JSON.stringify(transaction) === nextSerialized)
+ arr1.some(
+ (transaction) => JSON.stringify(transaction) === nextSerialized
+ )
) {
return arr1;
}
@@ -1283,9 +1285,7 @@ export function DashAgGrid(props) {
syncRowData();
} else {
setRowTransactionState(
- rowTransaction
- ? buildArray(rowTransaction, data)
- : [data]
+ rowTransaction ? buildArray(rowTransaction, data) : [data]
);
}
},
From fde08a012a7d02a497f2b2650fe56ea1f9ad6764 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 11:06:52 +0000
Subject: [PATCH 07/11] fix: de-duplicate queued row transactions by value
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/4f1937a5-6498-482d-81f2-16935edd8811
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/components/AgGrid.react.js | 9 ++++-----
src/lib/fragments/AgGrid.react.js | 9 ++++-----
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/src/lib/components/AgGrid.react.js b/src/lib/components/AgGrid.react.js
index e73e46a3..f03ffdbb 100644
--- a/src/lib/components/AgGrid.react.js
+++ b/src/lib/components/AgGrid.react.js
@@ -47,11 +47,10 @@ function DashAgGrid(props) {
return [arr2];
}
const nextSerialized = JSON.stringify(arr2);
- if (
- arr1.some(
- (transaction) => JSON.stringify(transaction) === nextSerialized
- )
- ) {
+ const serializedTransactions = new Set(
+ arr1.map((transaction) => JSON.stringify(transaction))
+ );
+ if (serializedTransactions.has(nextSerialized)) {
return arr1;
}
return [...arr1, arr2];
diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js
index 618aac15..e462fb7a 100644
--- a/src/lib/fragments/AgGrid.react.js
+++ b/src/lib/fragments/AgGrid.react.js
@@ -1258,11 +1258,10 @@ export function DashAgGrid(props) {
return [arr2];
}
const nextSerialized = JSON.stringify(arr2);
- if (
- arr1.some(
- (transaction) => JSON.stringify(transaction) === nextSerialized
- )
- ) {
+ const serializedTransactions = new Set(
+ arr1.map((transaction) => JSON.stringify(transaction))
+ );
+ if (serializedTransactions.has(nextSerialized)) {
return arr1;
}
return [...arr1, arr2];
From 80865ca22962106252468b04a6c2bdb54db3196e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 14:46:41 +0000
Subject: [PATCH 08/11] test: assert queued rowTransaction is released after
replay
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/e16d6d2f-7afa-4b7b-b3f2-a3020bb975b4
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
tests/test_selection_persistence.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index 1910a868..9fecdecd 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -153,6 +153,7 @@ def test_sp002_row_transaction_before_grid_ready(dash_duo):
app.layout = html.Div(
[
dcc.Interval(id="tick", interval=1, n_intervals=0, max_intervals=1),
+ html.Div(id="rowTransaction-state"),
dag.AgGrid(
id="grid",
rowData=rowData,
@@ -183,6 +184,12 @@ def apply_transaction(n):
]
}
+ @app.callback(
+ Output("rowTransaction-state", "children"), Input("grid", "rowTransaction")
+ )
+ def log_row_transaction(value):
+ return json.dumps(value)
+
dash_duo.start_server(app)
grid = utils.Grid(dash_duo, "grid")
@@ -190,3 +197,4 @@ def apply_transaction(n):
grid.wait_for_cell_text(1, 0, "Ford_0")
grid.wait_for_cell_text(2, 0, "Porsche_0")
grid.wait_for_cell_text(3, 0, "Queued_1")
+ dash_duo.wait_for_text_to_equal("#rowTransaction-state", "null")
From 67cf962568c0f331c3b6791428fb7e62f55d5e19 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 14:48:16 +0000
Subject: [PATCH 09/11] test: rename rowTransaction state callback for clarity
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/e16d6d2f-7afa-4b7b-b3f2-a3020bb975b4
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
tests/test_selection_persistence.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index 9fecdecd..12e533bf 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -187,7 +187,7 @@ def apply_transaction(n):
@app.callback(
Output("rowTransaction-state", "children"), Input("grid", "rowTransaction")
)
- def log_row_transaction(value):
+ def expose_row_transaction_state(value):
return json.dumps(value)
dash_duo.start_server(app)
From 988e9e05158d2e800c56aadaf359c4fcb9635323 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 15:30:49 +0000
Subject: [PATCH 10/11] fix: avoid duplicate rowTransaction replay on grid init
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/bbbb71ee-89ae-4307-a1ed-8b20e7eda41e
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
src/lib/fragments/AgGrid.react.js | 11 ++++++-----
tests/test_selection_persistence.py | 1 +
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/lib/fragments/AgGrid.react.js b/src/lib/fragments/AgGrid.react.js
index e462fb7a..c3074b42 100644
--- a/src/lib/fragments/AgGrid.react.js
+++ b/src/lib/fragments/AgGrid.react.js
@@ -1271,13 +1271,14 @@ export function DashAgGrid(props) {
(data) => {
const rowTransaction = rowTransactionState;
if (gridApi && !gridApi?.isDestroyed()) {
- if (rowTransaction) {
- rowTransaction.forEach((transaction) =>
- applyRowTransaction(transaction)
+ const isAlreadyQueued =
+ rowTransaction &&
+ rowTransaction.some((transaction) =>
+ equals(transaction, data)
);
- setRowTransactionState(null);
+ if (!isAlreadyQueued) {
+ applyRowTransaction(data);
}
- applyRowTransaction(data);
customSetProps({
rowTransaction: null,
});
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index 12e533bf..a5da4b87 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -197,4 +197,5 @@ def expose_row_transaction_state(value):
grid.wait_for_cell_text(1, 0, "Ford_0")
grid.wait_for_cell_text(2, 0, "Porsche_0")
grid.wait_for_cell_text(3, 0, "Queued_1")
+ grid.wait_for_rendered_rows(4)
dash_duo.wait_for_text_to_equal("#rowTransaction-state", "null")
From 12b3e1bde353974f1cc4c3e31777810349b4fe04 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Apr 2026 15:39:49 +0000
Subject: [PATCH 11/11] test: cover duplicate pre-mount rowTransaction
buffering
Agent-Logs-Url: https://github.com/plotly/dash-ag-grid/sessions/8740a8c3-4a89-42ff-9ca1-00070ea0647b
Co-authored-by: BSd3v <82055130+BSd3v@users.noreply.github.com>
---
tests/test_selection_persistence.py | 51 +++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/tests/test_selection_persistence.py b/tests/test_selection_persistence.py
index a5da4b87..8200c432 100644
--- a/tests/test_selection_persistence.py
+++ b/tests/test_selection_persistence.py
@@ -199,3 +199,54 @@ def expose_row_transaction_state(value):
grid.wait_for_cell_text(3, 0, "Queued_1")
grid.wait_for_rendered_rows(4)
dash_duo.wait_for_text_to_equal("#rowTransaction-state", "null")
+
+
+def test_sp003_duplicate_row_transaction_before_grid_ready(dash_duo):
+ app = Dash(__name__)
+
+ rowData = [
+ {"id": "Toyota_0", "make": "Toyota", "model": "Celica", "price": 35000},
+ {"id": "Ford_0", "make": "Ford", "model": "Mondeo", "price": 32000},
+ {"id": "Porsche_0", "make": "Porsche", "model": "Boxster", "price": 72000},
+ ]
+
+ app.layout = html.Div(
+ [
+ dcc.Interval(id="tick", interval=1, n_intervals=0, max_intervals=2),
+ dag.AgGrid(
+ id="grid",
+ rowData=rowData,
+ columnDefs=[
+ {"field": "id"},
+ {"field": "make"},
+ {"field": "model"},
+ {"field": "price"},
+ ],
+ defaultColDef={"flex": 1},
+ ),
+ ]
+ )
+
+ @app.callback(Output("grid", "rowTransaction"), Input("tick", "n_intervals"))
+ def apply_duplicate_transaction(n):
+ if not n:
+ return dash.no_update
+ return {
+ "add": [
+ {
+ "id": "Queued_1",
+ "make": "Queued",
+ "model": "Queued",
+ "price": 1,
+ }
+ ]
+ }
+
+ dash_duo.start_server(app)
+
+ grid = utils.Grid(dash_duo, "grid")
+ grid.wait_for_cell_text(0, 0, "Toyota_0")
+ grid.wait_for_cell_text(1, 0, "Ford_0")
+ grid.wait_for_cell_text(2, 0, "Porsche_0")
+ grid.wait_for_cell_text(3, 0, "Queued_1")
+ grid.wait_for_rendered_rows(4)