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
Support for wildcard attributes, implement data-* attribute #237
Changes from 2 commits
08f6263
8f2d3c2
54c9ef5
ec666eb
a4bb578
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
Component._prop_names = ('id', 'a', 'children', 'style', ) | ||
Component._type = 'TestComponent' | ||
Component._namespace = 'test_namespace' | ||
Component._valid_wildcard_attributes = ['data-', 'aria-'] | ||
|
||
|
||
def nested_tree(): | ||
|
@@ -411,6 +412,25 @@ def to_dict(id, children): | |
) | ||
""" | ||
|
||
def test_to_plotly_json_with_wildcards(self): | ||
c = Component(id='a', **{'aria-expanded': 'true', | ||
'data-toggle': 'toggled', | ||
'data-none': None}) | ||
c._prop_names = ('id',) | ||
c._type = 'MyComponent' | ||
c._namespace = 'basic' | ||
self.assertEqual( | ||
c.to_plotly_json(), | ||
{'namespace': 'basic', | ||
'props': { | ||
'aria-expanded': 'true', | ||
'data-toggle': 'toggled', | ||
'data-none': None, | ||
'id': 'a', | ||
}, | ||
'type': 'MyComponent'} | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
def test_len(self): | ||
self.assertEqual(len(Component()), 0) | ||
self.assertEqual(len(Component(children='Hello World')), 1) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
from multiprocessing import Value | ||
import datetime | ||
import itertools | ||
import re | ||
import dash_html_components as html | ||
import dash_core_components as dcc | ||
import dash_flow_example | ||
|
@@ -67,6 +70,60 @@ def update_output(value): | |
|
||
assert_clean_console(self) | ||
|
||
def test_wildcard_callback(self): | ||
app = dash.Dash(__name__) | ||
app.layout = html.Div([ | ||
dcc.Input( | ||
id='input', | ||
value='initial value' | ||
), | ||
html.Div( | ||
html.Div([ | ||
1.5, | ||
None, | ||
'string', | ||
html.Div(id='output-1', **{'data-cb': 'initial value', | ||
'aria-cb': 'initial value'}) | ||
]) | ||
) | ||
]) | ||
|
||
input_call_count = Value('i', 0) | ||
|
||
@app.callback(Output('output-1', 'data-cb'), [Input('input', 'value')]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very cool that this works |
||
def update_data(value): | ||
input_call_count.value = input_call_count.value + 1 | ||
return value | ||
|
||
@app.callback(Output('output-1', 'children'), | ||
[Input('output-1', 'data-cb')]) | ||
def update_text(data): | ||
return data | ||
|
||
self.startServer(app) | ||
output1 = self.wait_for_element_by_id('output-1') | ||
wait_for(lambda: output1.text == 'initial value') | ||
self.percy_snapshot(name='wildcard-callback-1') | ||
|
||
input1 = self.wait_for_element_by_id('input') | ||
input1.clear() | ||
|
||
input1.send_keys('hello world') | ||
|
||
output1 = lambda: self.wait_for_element_by_id('output-1') | ||
wait_for(lambda: output1().text == 'hello world') | ||
self.percy_snapshot(name='wildcard-callback-2') | ||
|
||
self.assertEqual( | ||
input_call_count.value, | ||
# an initial call | ||
1 + | ||
# one for each hello world character | ||
len('hello world') | ||
) | ||
|
||
assert_clean_console(self) | ||
|
||
def test_aborted_callback(self): | ||
"""Raising PreventUpdate prevents update and triggering dependencies""" | ||
|
||
|
@@ -116,6 +173,61 @@ def callback2(value): | |
|
||
self.percy_snapshot(name='aborted') | ||
|
||
def test_wildcard_data_attributes(self): | ||
app = dash.Dash() | ||
app.layout = html.Div([ | ||
html.Div( | ||
id="inner-element", | ||
**{ | ||
'data-string': 'multiple words', | ||
'data-number': 512, | ||
'data-none': None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value will not be rendered in the html div. |
||
'data-date': datetime.datetime(2012, 1, 10), | ||
'aria-progress': 5 | ||
} | ||
) | ||
], id='data-element') | ||
|
||
self.startServer(app) | ||
|
||
div = self.wait_for_element_by_id('data-element') | ||
|
||
# React wraps text and numbers with e.g. <!-- react-text: 20 --> | ||
# Remove those | ||
comment_regex = '<!--[^\[](.*?)-->' | ||
|
||
# Somehow the html attributes are unordered. | ||
# Try different combinations (they're all valid html) | ||
permutations = itertools.permutations([ | ||
'id="inner-element"', | ||
'data-string="multiple words"', | ||
'data-number="512"', | ||
'data-date="2012-01-10"', | ||
'aria-progress="5"' | ||
], 5) | ||
passed = False | ||
for i, permutation in enumerate(permutations): | ||
actual_cleaned = re.sub(comment_regex, '', | ||
div.get_attribute('innerHTML')) | ||
expected_cleaned = re.sub( | ||
comment_regex, | ||
'', | ||
"<div PERMUTE></div>" | ||
.replace('PERMUTE', ' '.join(list(permutation))) | ||
) | ||
passed = passed or (actual_cleaned == expected_cleaned) | ||
if passed: | ||
break | ||
if not passed: | ||
raise Exception( | ||
'HTML does not match\nActual:\n{}\n\nExpected:\n{}'.format( | ||
actual_cleaned, | ||
expected_cleaned | ||
) | ||
) | ||
|
||
assert_clean_console(self) | ||
|
||
def test_flow_component(self): | ||
app = dash.Dash() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, way better 👍