Skip to content

Commit 96969d1

Browse files
authored
Merge pull request #3669 from Francisc0C/dev
Fix #3030: Selection for DataTable cleared with custom action settings
2 parents 0caed70 + 744d86d commit 96969d1

3 files changed

Lines changed: 121 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
55
## [UNRELEASED]
66

77
## Added
8+
- [#3669](https://github.com/plotly/dash/pull/3669) Selection for DataTable cleared with custom action settings
89
- [#3680](https://github.com/plotly/dash/pull/3680) Added `search_order` prop to `Dropdown` to allow users to preserve original option order during search
910
- Added `csrf_token_name` and `csrf_header_name` config options to allow configuring the CSRF cookie and header names. Fixes [#729](https://github.com/plotly/dash/issues/729)
1011

components/dash-table/src/dash-table/components/Table/derivedPropsHelper.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export default () => {
1818
page_current,
1919
page_size
2020
]);
21+
const selectedRowsCache = memoizeOneWithFlag(
22+
selected_rows => selected_rows
23+
);
2124
const sortCache = memoizeOneWithFlag(sort => sort);
2225
const viewportCache = memoizeOneWithFlag(viewport => viewport);
2326
const viewportSelectedColumnsCache = memoizeOneWithFlag(
@@ -37,6 +40,7 @@ export default () => {
3740
page_action,
3841
page_current,
3942
page_size,
43+
selected_rows,
4044
sort_action,
4145
sort_by,
4246
viewport,
@@ -64,17 +68,19 @@ export default () => {
6468
const invalidatedFilter = filterCache(filter_query);
6569
const invalidatedPagination = paginationCache(page_current, page_size);
6670
const invalidatedSort = sortCache(sort_by);
71+
const invalidatedSelectedRows = selectedRowsCache(selected_rows);
6772

6873
const invalidateSelection =
69-
(!invalidatedFilter.cached &&
74+
invalidatedSelectedRows.cached &&
75+
((!invalidatedFilter.cached &&
7076
!invalidatedFilter.first &&
7177
filter_action.type === TableAction.Custom) ||
72-
(!invalidatedPagination.cached &&
73-
!invalidatedPagination.first &&
74-
page_action === TableAction.Custom) ||
75-
(!invalidatedSort.cached &&
76-
!invalidatedSort.first &&
77-
sort_action === TableAction.Custom);
78+
(!invalidatedPagination.cached &&
79+
!invalidatedPagination.first &&
80+
page_action === TableAction.Custom) ||
81+
(!invalidatedSort.cached &&
82+
!invalidatedSort.first &&
83+
sort_action === TableAction.Custom));
7884

7985
const newProps: Partial<SanitizedAndDerivedProps> = {};
8086

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import dash
2+
from dash.dependencies import Input, Output
3+
from dash import html
4+
from dash.dash_table import DataTable
5+
6+
import json
7+
import time
8+
import pandas as pd
9+
10+
url = "https://github.com/plotly/datasets/raw/master/" "26k-consumer-complaints.csv"
11+
rawDf = pd.read_csv(url, nrows=100)
12+
rawDf["id"] = rawDf.index + 3000
13+
df = rawDf.to_dict("records")
14+
15+
16+
def get_app():
17+
app = dash.Dash(__name__)
18+
19+
app.layout = html.Div(
20+
[
21+
DataTable(
22+
id="table",
23+
columns=[{"name": i, "id": i} for i in rawDf.columns],
24+
data=df,
25+
row_selectable="multi",
26+
selected_rows=[],
27+
filter_action="custom",
28+
filter_query="",
29+
sort_action="custom",
30+
sort_by=[],
31+
page_action="custom",
32+
page_current=0,
33+
page_size=10,
34+
style_cell=dict(width=100, min_width=100, max_width=100),
35+
),
36+
html.Button("Set selected + sort_by", id="sort"),
37+
html.Button("Set selected + filter", id="filter"),
38+
html.Button("Set selected + page", id="page"),
39+
html.Div(id="selected_rows_output"),
40+
]
41+
)
42+
43+
@app.callback(
44+
Output("selected_rows_output", "children"),
45+
Input("table", "selected_rows"),
46+
)
47+
def show_selected_rows(selected_rows):
48+
return json.dumps(selected_rows) if selected_rows is not None else "None"
49+
50+
@app.callback(
51+
Output("table", "selected_rows"),
52+
Output("table", "sort_by"),
53+
Input("sort", "n_clicks"),
54+
prevent_initial_call=True,
55+
)
56+
def set_selected_and_sort(_):
57+
return [0, 1, 2], [{"column_id": rawDf.columns[0], "direction": "asc"}]
58+
59+
@app.callback(
60+
Output("table", "selected_rows", allow_duplicate=True),
61+
Output("table", "filter_query"),
62+
Input("filter", "n_clicks"),
63+
prevent_initial_call=True,
64+
)
65+
def set_selected_and_filter(_):
66+
return [0, 1, 2], "{} > 1".format(rawDf.columns[0])
67+
68+
@app.callback(
69+
Output("table", "selected_rows", allow_duplicate=True),
70+
Output("table", "page_current"),
71+
Input("page", "n_clicks"),
72+
prevent_initial_call=True,
73+
)
74+
def set_selected_and_page(_):
75+
return [0, 1, 2], 1
76+
77+
return app
78+
79+
80+
def test_tsrc001_selected_rows_persists_with_sort_by(test):
81+
test.start_server(get_app())
82+
83+
test.find_element("#sort").click()
84+
time.sleep(1)
85+
86+
assert test.find_element("#selected_rows_output").text == json.dumps([0, 1, 2])
87+
assert test.get_log_errors() == []
88+
89+
90+
def test_tsrc002_selected_rows_persists_with_filter_query(test):
91+
test.start_server(get_app())
92+
93+
test.find_element("#filter").click()
94+
time.sleep(1)
95+
96+
assert test.find_element("#selected_rows_output").text == json.dumps([0, 1, 2])
97+
assert test.get_log_errors() == []
98+
99+
100+
def test_tsrc003_selected_rows_persists_with_page_current(test):
101+
test.start_server(get_app())
102+
103+
test.find_element("#page").click()
104+
time.sleep(1)
105+
106+
assert test.find_element("#selected_rows_output").text == json.dumps([0, 1, 2])
107+
assert test.get_log_errors() == []

0 commit comments

Comments
 (0)