Skip to content

Commit 6cfc62f

Browse files
authored
Merge pull request #404 from plotly/backport/column-state-fix
Backport column state fix
2 parents 5f5d546 + 46603dd commit 6cfc62f

File tree

4 files changed

+247
-34
lines changed

4 files changed

+247
-34
lines changed

src/lib/components/AgGrid.react.js

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import PropTypes from 'prop-types';
22
import LazyLoader from '../LazyLoader';
33
import React, {lazy, Suspense, useState, useCallback, useEffect} from 'react';
4+
import {pick} from 'ramda';
45

56
const RealAgGrid = lazy(LazyLoader.agGrid);
67
const RealAgGridEnterprise = lazy(LazyLoader.agGridEnterprise);
@@ -9,6 +10,25 @@ function getGrid(enable) {
910
return enable ? RealAgGridEnterprise : RealAgGrid;
1011
}
1112

13+
export const defaultProps = {
14+
className: 'ag-theme-alpine',
15+
resetColumnState: false,
16+
exportDataAsCsv: false,
17+
selectAll: false,
18+
deselectAll: false,
19+
enableEnterpriseModules: false,
20+
updateColumnState: false,
21+
persisted_props: ['selectedRows'],
22+
persistence_type: 'local',
23+
suppressDragLeaveHidesColumns: true,
24+
dangerously_allow_code: false,
25+
rowModelType: 'clientSide',
26+
dashGridOptions: {},
27+
filterModel: {},
28+
paginationGoTo: null,
29+
selectedRows: [],
30+
};
31+
1232
/**
1333
* Dash interface to AG Grid, a powerful tabular data component.
1434
*/
@@ -45,31 +65,26 @@ function DashAgGrid(props) {
4565

4666
return (
4767
<Suspense fallback={null}>
48-
<RealComponent parentState={state} {...props} />
68+
<RealComponent parentState={state} {...defaultProps} {...props} />
4969
</Suspense>
5070
);
5171
}
5272

73+
const REACT_VERSION_DASH2_COMPAT = 18.3;
74+
if (
75+
parseFloat(React.version.substring(0, React.version.lastIndexOf('.'))) <
76+
REACT_VERSION_DASH2_COMPAT
77+
) {
78+
DashAgGrid.defaultProps = defaultProps;
79+
} else {
80+
DashAgGrid.dashPersistence = pick(
81+
['persisted_props', 'persistence_type'],
82+
defaultProps
83+
);
84+
}
85+
5386
DashAgGrid.dashRenderType = true;
5487

55-
DashAgGrid.defaultProps = {
56-
className: 'ag-theme-alpine',
57-
resetColumnState: false,
58-
exportDataAsCsv: false,
59-
selectAll: false,
60-
deselectAll: false,
61-
enableEnterpriseModules: false,
62-
updateColumnState: false,
63-
persisted_props: ['selectedRows'],
64-
persistence_type: 'local',
65-
suppressDragLeaveHidesColumns: true,
66-
dangerously_allow_code: false,
67-
rowModelType: 'clientSide',
68-
dashGridOptions: {},
69-
filterModel: {},
70-
paginationGoTo: null,
71-
selectedRows: [],
72-
};
7388
DashAgGrid.propTypes = {
7489
/********************************
7590
* DASH PROPS
@@ -751,7 +766,6 @@ DashAgGrid.propTypes = {
751766
};
752767

753768
export const propTypes = DashAgGrid.propTypes;
754-
export const defaultProps = DashAgGrid.defaultProps;
755769

756770
export default DashAgGrid;
757771

src/lib/fragments/AgGrid.react.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ import {
1515
assoc,
1616
assocPath,
1717
} from 'ramda';
18-
import {
19-
propTypes as _propTypes,
20-
defaultProps as _defaultProps,
21-
apiGetters,
22-
} from '../components/AgGrid.react';
18+
import {propTypes as _propTypes, apiGetters} from '../components/AgGrid.react';
2319
import {
2420
COLUMN_DANGEROUS_FUNCTIONS,
2521
COLUMN_MAYBE_FUNCTIONS,
@@ -1342,10 +1338,10 @@ export function DashAgGrid(props) {
13421338

13431339
// Handle gridApi initialization - column state application
13441340
useEffect(() => {
1345-
if (gridApi && gridApi !== prevGridApi && props.columnState) {
1341+
if (gridApi && gridApi !== prevGridApi && columnState_push) {
13461342
setColumnState();
13471343
}
1348-
}, [gridApi, prevGridApi, props.columnState, setColumnState]);
1344+
}, [gridApi, prevGridApi, columnState_push]);
13491345

13501346
// Handle gridApi initialization - finalization
13511347
useEffect(() => {
@@ -1378,7 +1374,7 @@ export function DashAgGrid(props) {
13781374
setColumnState_push(true);
13791375
}
13801376
}
1381-
}, [props.columnState, props.loading_state, columnState_push]);
1377+
}, [props.columnState, props.loading_state]);
13821378

13831379
// Handle ID changes
13841380
useEffect(() => {
@@ -1605,11 +1601,9 @@ export function DashAgGrid(props) {
16051601
);
16061602
}
16071603

1608-
DashAgGrid.defaultProps = _defaultProps;
16091604
DashAgGrid.propTypes = {parentState: PropTypes.any, ..._propTypes};
16101605

16111606
export const propTypes = DashAgGrid.propTypes;
1612-
export const defaultProps = DashAgGrid.defaultProps;
16131607

16141608
var dagfuncs = (window.dash_ag_grid = window.dash_ag_grid || {});
16151609
dagfuncs.useGridFilter = useGridFilter;

src/lib/utils/propCategories.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ export const PASSTHRU_PROPS = ['rowData'];
321321
*/
322322
export const PROPS_NOT_FOR_AG_GRID = [
323323
'children',
324+
'dashRenderType',
325+
'licenseKey',
324326
'setProps',
325327
'loading_state',
326328
'enableEnterpriseModules',
@@ -354,6 +356,7 @@ export const PROPS_NOT_FOR_AG_GRID = [
354356
'scrollTo',
355357
'eventListeners',
356358
'eventData',
359+
'paginationInfo',
357360
];
358361

359362
/**

tests/test_column_state.py

Lines changed: 206 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from dash import Dash, html, Output, Input, no_update, State, ctx
1+
from dash import Dash, html, Output, Input, no_update, State, ctx, Patch
22
import dash_ag_grid as dag
33
import plotly.express as px
44
import json
@@ -84,7 +84,7 @@
8484
alt_colState = [
8585
{
8686
"colId": "price",
87-
"width": 198,
87+
"width": 200,
8888
"hide": False,
8989
"pinned": None,
9090
"sort": "asc",
@@ -239,7 +239,7 @@ def loadState(n):
239239

240240
dash_duo.find_element("#get-column-state-button").click()
241241
testState = colState.copy()
242-
testState[1]["width"] = 198
242+
testState[1]["width"] = 200
243243
until(
244244
lambda: json.dumps(testState)
245245
in dash_duo.find_element("#reset-column-state-grid-pre").text,
@@ -315,4 +315,206 @@ def make_grid(n):
315315
for x in range(10):
316316
dash_duo.find_element("#add-grid").click()
317317
time.sleep(2) # pausing to emulate separation because user inputs
318-
assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == []
318+
assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == []
319+
320+
def test_cs003_column_state(dash_duo):
321+
data = [
322+
{
323+
"localTime": "5:00am",
324+
"a": 0.231,
325+
"b": 0.523,
326+
"c": 0.423,
327+
"d": 0.527,
328+
},
329+
{
330+
"localTime": "5:15am",
331+
"a": 0.423,
332+
"b": 0.452,
333+
"c": 0.523,
334+
"d": 0.543,
335+
},
336+
{
337+
"localTime": "5:30am",
338+
"a": 0.537,
339+
"b": 0.246,
340+
"c": 0.426,
341+
"d": 0.421,
342+
},
343+
{
344+
"localTime": "5:45am",
345+
"a": 0.893,
346+
"b": 0.083,
347+
"c": 0.532,
348+
"d": 0.983,
349+
},
350+
{
351+
"localTime": "6:00am",
352+
"a": 0.231,
353+
"b": 0.523,
354+
"c": 0.423,
355+
"d": 0.527,
356+
},
357+
{
358+
"localTime": "6:15am",
359+
"a": 0.423,
360+
"b": 0.452,
361+
"c": 0.523,
362+
"d": 0.543,
363+
},
364+
{
365+
"localTime": "6:30am",
366+
"a": 0.537,
367+
"b": 0.246,
368+
"c": 0.426,
369+
"d": 0.421,
370+
},
371+
{
372+
"localTime": "6:45am",
373+
"a": 0.893,
374+
"b": 0.083,
375+
"c": 0.532,
376+
"d": 0.983,
377+
},
378+
{
379+
"localTime": "7:00am",
380+
"a": 0.231,
381+
"b": 0.523,
382+
"c": 0.423,
383+
"d": 0.527,
384+
},
385+
{
386+
"localTime": "7:15am",
387+
"a": 0.423,
388+
"b": 0.452,
389+
"c": 0.523,
390+
"d": 0.543,
391+
},
392+
{
393+
"localTime": "7:30am",
394+
"a": 0.537,
395+
"b": 0.246,
396+
"c": 0.426,
397+
"d": 0.421,
398+
},
399+
{
400+
"localTime": "7:45am",
401+
"a": 0.893,
402+
"b": 0.083,
403+
"c": 0.532,
404+
"d": 0.983,
405+
},
406+
{
407+
"localTime": "8:00am",
408+
"a": 0.231,
409+
"b": 0.523,
410+
"c": 0.423,
411+
"d": 0.527,
412+
},
413+
{
414+
"localTime": "8:15am",
415+
"a": 0.423,
416+
"b": 0.452,
417+
"c": 0.523,
418+
"d": 0.543,
419+
},
420+
{
421+
"localTime": "8:30am",
422+
"a": 0.537,
423+
"b": 0.246,
424+
"c": 0.426,
425+
"d": 0.421,
426+
},
427+
{
428+
"localTime": "8:45am",
429+
"a": 0.893,
430+
"b": 0.083,
431+
"c": 0.532,
432+
"d": 0.983,
433+
},
434+
{
435+
"localTime": "8:00am",
436+
"a": 0.231,
437+
"b": 0.523,
438+
"c": 0.423,
439+
"d": 0.527,
440+
},
441+
{
442+
"localTime": "8:15am",
443+
"a": 0.423,
444+
"b": 0.452,
445+
"c": 0.523,
446+
"d": 0.543,
447+
},
448+
{
449+
"localTime": "8:30am",
450+
"a": 0.537,
451+
"b": 0.246,
452+
"c": 0.426,
453+
"d": 0.421,
454+
},
455+
{
456+
"localTime": "8:45am",
457+
"a": 0.893,
458+
"b": 0.083,
459+
"c": 0.532,
460+
"d": 0.983,
461+
},
462+
]
463+
464+
columnDefs = [
465+
{"field": "localTime"},
466+
{"field": "a"},
467+
{"field": "b"},
468+
{"field": "c"},
469+
{"field": "d"},
470+
]
471+
472+
app = Dash(__name__)
473+
474+
app.layout = html.Div(
475+
[
476+
html.Div(
477+
[
478+
html.Button("Remove Column", id="remove-column", n_clicks=0),
479+
html.Div(
480+
id="grid-holder",
481+
children=[
482+
dag.AgGrid(
483+
id=f"grid",
484+
columnDefs=columnDefs,
485+
rowData=data,
486+
columnSize="autoSize",
487+
)
488+
],
489+
),
490+
],
491+
),
492+
]
493+
)
494+
495+
@app.callback(
496+
Output("grid", "columnDefs"),
497+
Input("remove-column", "n_clicks"),
498+
)
499+
def remove_column(n):
500+
if n:
501+
cols = Patch()
502+
if n < 3:
503+
del cols[0]
504+
return cols
505+
return no_update
506+
507+
dash_duo.start_server(
508+
app,
509+
debug=True,
510+
use_reloader=False,
511+
use_debugger=True,
512+
dev_tools_hot_reload=False,
513+
dev_tools_props_check=True,
514+
dev_tools_disable_version_check=True,
515+
)
516+
517+
for x in range(3):
518+
dash_duo.find_element("#remove-column").click()
519+
time.sleep(2) # pausing to emulate separation because user inputs
520+
assert list(filter(lambda i: i.get("level") != "ERROR", dash_duo.get_logs())) == []

0 commit comments

Comments
 (0)