Skip to content

PersistenceTransforms for date in datePickerRange and datePickerSingle #1376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
56f0a0e
add logic for checking for persisted prop in propName and propPart
harryturr Aug 18, 2020
4f07812
add persistence checking defining date picker single inside of callback
harryturr Aug 20, 2020
4479682
add persistence test checking defining date picker range inside of ca…
harryturr Aug 20, 2020
6937f1c
lint
harryturr Aug 21, 2020
65938ac
remove comments / sleep
harryturr Aug 21, 2020
94b91c7
rename functions
harryturr Aug 21, 2020
951a767
update circleci config to point to dcc branch
harryturr Aug 21, 2020
b180d00
Merge branch 'dev' into persistence-hg
harryturr Aug 21, 2020
a5e0874
Merge branch 'dev' into persistence-hg
harryturr Aug 25, 2020
54f6229
Merge branch 'dev' into persistence-hg
harryturr Aug 25, 2020
9b60d4a
update circleci to new dcc branch
harryturr Aug 25, 2020
59bbf30
Update .circleci/config.yml
harryturr Aug 26, 2020
900af6f
Update .circleci/config.yml
harryturr Aug 26, 2020
109ea46
typo
harryturr Aug 26, 2020
fe58b2a
functions for interaction with date pickers, rm print statements
harryturr Aug 26, 2020
91c5324
simplify check for PropName
harryturr Aug 26, 2020
5c11477
add dash-generator-test-component-persisted
harryturr Aug 27, 2020
1f04bc5
remove date picker tests
harryturr Aug 27, 2020
424b6dd
rm obsolete imports
harryturr Aug 28, 2020
0cb4d02
update test persisted component
harryturr Aug 31, 2020
9c5e33d
add test components for persisted props and nested persisted props
harryturr Aug 31, 2020
963031b
add persistenceTransforms test for prop and nested prop
harryturr Sep 1, 2020
4c47006
add build for test compenent in package.json
harryturr Sep 1, 2020
95af752
Merge branch 'dev' into persistence-hg
harryturr Sep 1, 2020
738a9ec
simplify test component props and dependencies
harryturr Sep 1, 2020
0b97608
Merge branch 'dev' into persistence-hg
harryturr Sep 1, 2020
e541e62
add build for MyPersistedComponentNested
harryturr Sep 1, 2020
3c3efbf
update name
harryturr Sep 2, 2020
1586634
Merge branch 'dev' into persistence-hg
harryturr Sep 2, 2020
b063d60
add test persistence components to @plotly/dash-test-components
harryturr Sep 2, 2020
e2cab41
update package.json
harryturr Sep 2, 2020
06d77d3
rm old persisted test components
harryturr Sep 2, 2020
2b240e2
update imports for test_persistence
harryturr Sep 2, 2020
e4026e6
remove old r builds
harryturr Sep 2, 2020
5601ec3
update component comment description
harryturr Sep 2, 2020
1596d43
Merge branch 'dev' into persistence-hg
harryturr Sep 3, 2020
37d454f
remove unnecessary props from test components
harryturr Sep 4, 2020
f7c04fd
remove dcc branch from ci
harryturr Sep 4, 2020
6f2ce30
edit code style with conditional chaining
harryturr Sep 4, 2020
b5f78b3
update CHANGELOG.md
harryturr Sep 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ jobs:
command: |
. venv/bin/activate && pip install --no-cache-dir --upgrade -e . --progress-bar off && mkdir packages
cd dash-renderer && renderer build && python setup.py sdist && mv dist/* ../packages/ && cd ..
git clone --depth 1 https://github.com/plotly/dash-core-components.git
git clone -b hg-700-persistence --depth 1 https://github.com/plotly/dash-core-components.git
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be unnecessary now that we test against a custom-made component

cd dash-core-components && npm ci && npm run build && python setup.py sdist && mv dist/* ../packages/ && cd ..
ls -la packages
- persist_to_workspace:
Expand Down Expand Up @@ -205,7 +205,7 @@ jobs:
command: |
source venv/Scripts/activate && pip install --no-cache-dir --upgrade -e . --progress-bar off && mkdir packages
cd dash-renderer && renderer build && python setup.py sdist && mv dist/* ../packages/ && cd ..
git clone --depth 1 https://github.com/plotly/dash-core-components.git
git clone -b persistence-hg --depth 1 https://github.com/plotly/dash-core-components.git
cd dash-core-components && npm ci && npm run build && python setup.py sdist && mv dist/* ../packages/ && cd ..
ls -la packages
- persist_to_workspace:
Expand Down Expand Up @@ -234,7 +234,7 @@ jobs:
pip install --no-cache-dir --upgrade -e .[dev,testing] --progress-bar off
git clone --depth 1 https://github.com/plotly/dashR.git -b dev dashR
git clone --depth 1 https://github.com/plotly/dash-html-components.git
git clone --depth 1 https://github.com/plotly/dash-core-components.git
git clone -b persistence-hg --depth 1 https://github.com/plotly/dash-core-components.git
git clone --depth 1 https://github.com/plotly/dash-table.git
shopt -s extglob
cd dash-html-components; npm ci && npm run build; rm -rf !(.|..|DESCRIPTION|LICENSE.txt|LICENSE|NAMESPACE|.Rbuildignore|R|man|inst|vignettes|build)
Expand Down
20 changes: 16 additions & 4 deletions dash-renderer/src/persistence.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,22 @@ const noopTransform = {
apply: (storedValue, _propValue) => storedValue,
};

const getTransform = (element, propName, propPart) =>
propPart
? element.persistenceTransforms[propName][propPart]
: noopTransform;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old logic checks for just propPart for persistenceTransforms, but here we add logic to check if either propName or propPart is the persisted prop.

const getTransform = (element, propName, propPart) => {
if (typeof element.persistenceTransforms !== 'undefined') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can simplify the top level check to:

Suggested change
if (typeof element.persistenceTransforms !== 'undefined') {
if (element.persistenceTransforms) {

any truth-y value will pass the test (https://dorey.github.io/JavaScript-Equality-Table/)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (
Object.getOwnPropertyNames(element.persistenceTransforms).includes(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified to if(element.persistenceTransforms[propName]) in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propName
)
) {
if (propPart) {
return element.persistenceTransforms[propName][propPart];
}
return element.persistenceTransforms[propName];
}
return noopTransform;
}
return noopTransform;
};

const getValsKey = (id, persistedProp, persistence) =>
`${stringifyId(id)}.${persistedProp}.${JSON.stringify(persistence)}`;
Expand Down
192 changes: 192 additions & 0 deletions tests/integration/renderer/test_persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
import dash_html_components as html
import dash_table as dt

from datetime import datetime
from datetime import timedelta as td


from selenium.webdriver.common.action_chains import ActionChains


@pytest.fixture(autouse=True)
def clear_storage(dash_duo):
Expand Down Expand Up @@ -526,3 +532,189 @@ def set_out(val):
dash_duo.wait_for_text_to_equal(".out", "")

dash_duo.find_element("#btn").click()


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test persistence for DatePickerSingle and DatePicker range in callbacks, in conjunction with plotly/dash-core-components#848.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think it's good to have at least one test that checks that persistence works as expected with and without a part, the tests validating the behavior of DPSingle and DPRange should be moved to DCC.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could create a new test component similar to https://github.com/plotly/dash/tree/dev/%40plotly/dash-generator-test-component-standard that has both (1) prop part, (2) no prop part for two different persisted props, and we can validate the behavior against that component instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def test_rdps013_persisted_dps(dash_duo):
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.Button("fire callback", id="btn"),
html.Div(
children=[
dcc.DatePickerSingle(
id="dps1",
date=datetime.today(),
persistence=True,
persistence_type="session",
),
html.P("dps1", id="dps1-p"),
html.Div(id="container"),
html.P("dps2", id="dps2-p"),
]
),
]
)

@app.callback(Output("container", "children"), [Input("btn", "n_clicks")])
def update_output(value):
return dcc.DatePickerSingle(
id="dps2",
date=datetime.today(),
persistence=True,
persistence_type="session",
)

@app.callback(Output("dps1-p", "children"), [Input("dps1", "date")])
def display_dps1(value):
print(value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔪

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return value

@app.callback(Output("dps2-p", "children"), [Input("dps2", "date")])
def display_dps2(value):
return value

dash_duo.start_server(app)
dps1 = dash_duo.find_element("#dps1")
dps2 = dash_duo.find_element("#dps2")

(
ActionChains(dash_duo.driver)
.move_to_element(dps1)
.pause(0.2)
.click(dps1)
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys(Keys.DELETE)
.send_keys("01/01/2020")
.send_keys(Keys.ENTER)
).perform()

dash_duo.wait_for_text_to_equal("#dps1-p", "2020-01-01")

(
ActionChains(dash_duo.driver)
.move_to_element(dps2)
.pause(0.2)
.click(dps2)
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys(Keys.DELETE)
.send_keys("01/01/2020")
.send_keys(Keys.ENTER)
Copy link
Contributor

@Marc-Andre-Rivet Marc-Andre-Rivet Aug 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY 🌴🐫

def set_date(target, date):
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

).perform()

dash_duo.wait_for_text_to_equal("#dps2-p", "2020-01-01")

dash_duo.find_element("#btn").click()

dash_duo.wait_for_text_to_equal("#dps1-p", "2020-01-01")
dash_duo.wait_for_text_to_equal("#dps2-p", "2020-01-01")


def test_rdps014_persisted_dpr(dash_duo):
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.Button("fire callback", id="btn"),
html.Div(
children=[
dcc.DatePickerRange(
id="dpr1",
start_date=datetime.today() - td(days=3),
end_date=datetime.today(),
persistence=True,
persistence_type="session",
),
html.P("dpr1", id="dpr1-p-start"),
html.P("dpr1", id="dpr1-p-end"),
html.Div(id="container"),
html.P("dpr2", id="dpr2-p-start"),
html.P("dpr2", id="dpr2-p-end"),
]
),
]
)

@app.callback(Output("container", "children"), [Input("btn", "n_clicks")])
def update_output(value):
return dcc.DatePickerRange(
id="dpr2",
start_date=datetime.today() - td(days=3),
end_date=datetime.today(),
persistence=True,
persistence_type="session",
)

@app.callback(Output("dpr1-p-start", "children"), [Input("dpr1", "start_date")])
def display_dps1(value):
print(value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔪

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return value

@app.callback(Output("dpr1-p-end", "children"), [Input("dpr1", "end_date")])
def display_dps1(value):
print(value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔪

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return value

@app.callback(Output("dpr2-p-start", "children"), [Input("dpr2", "start_date")])
def display_dps2(value):
return value

@app.callback(Output("dpr2-p-end", "children"), [Input("dpr2", "end_date")])
def display_dps2(value):
return value

dash_duo.start_server(app)
dpr1 = dash_duo.find_element("div#dpr1 div div div div .DateInput_input")
dpr2 = dash_duo.find_element("div#dpr2 div div div div .DateInput_input")

(
ActionChains(dash_duo.driver)
.move_to_element(dpr1)
.click(dpr1)
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys("01/01/2020")
.pause(0.2)
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys("01/02/2020")
).perform()

dash_duo.wait_for_text_to_equal("#dpr1-p-start", "2020-01-01")
dash_duo.wait_for_text_to_equal("#dpr1-p-end", "2020-01-02")

(
ActionChains(dash_duo.driver)
.move_to_element(dpr2)
.click(dpr2)
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys("01/01/2020")
.pause(0.2)
# .send_keys("01/02/2020")
.send_keys(Keys.END)
.key_down(Keys.SHIFT)
.send_keys(Keys.HOME)
.key_up(Keys.SHIFT)
.send_keys("01/02/2020")
Copy link
Contributor

@Marc-Andre-Rivet Marc-Andre-Rivet Aug 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY 🌴🐫

def set_date_range(target, start_date, end_date):
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also dash_duo.clear_input that would simplify some of these (assuming there isn't some reason it fails for datepickers)

dash_duo.clear_input("#persistence-val")

).perform()

dash_duo.wait_for_text_to_equal("#dpr2-p-start", "2020-01-01")
dash_duo.wait_for_text_to_equal("#dpr2-p-end", "2020-01-02")

dash_duo.find_element("#btn").click()

dash_duo.wait_for_text_to_equal("#dpr1-p-start", "2020-01-01")
dash_duo.wait_for_text_to_equal("#dpr1-p-end", "2020-01-02")
dash_duo.wait_for_text_to_equal("#dpr2-p-start", "2020-01-01")
dash_duo.wait_for_text_to_equal("#dpr2-p-end", "2020-01-02")