Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Commit 1b713d4

Browse files
Merge pull request #185 from plotly/addMissingInputDisabledAttr
Add missing input disabled attr
2 parents 86bc66f + 0be4e4c commit 1b713d4

File tree

11 files changed

+207
-77
lines changed

11 files changed

+207
-77
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [0.22.2] - 2018-05-22
6+
### Fixed
7+
- `dcc.Input` component now handles `disabled=False` property.
8+
- Broken sourcemaps for debugging.
9+
### Added
10+
- Testing configuration for CHROMEPATH and SERVER_PROCESSES
11+
512
## [0.22.1] - 2018-04-09
613
### Fixed
714
- Various bugs with the `ohlc` and `candlestick` chart type in the `dcc.Graph`

CONTRIBUTING.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,16 @@ like this:
1717
```
1818
export TOX_PYTHON_27=~/.pyenv/versions/2.7.14/bin/python
1919
```
20+
21+
## Local configuration
22+
You can configure the test server with the following variables:
23+
### DASH_TEST_CHROMEPATH
24+
If you run a special chrome set the path to your chrome binary with this environment variable.
25+
26+
### DASH_TEST_PROCESSES
27+
If you encounter errors about Multi-server + Multi-processing when running under Python 3 try running the tests with the number of server processes set to 1.
28+
29+
### Example: single test run with configuration
30+
```
31+
DASH_TEST_CHROMEPATH=/bin/google-chrome-beta DASH_TEST_PROCESSES=1 python -m unittest -v test.test_integration.Tests.test_inputs
32+
```

circle.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ machine:
77
TOX_PYTHON_27: python2.7
88

99
dependencies:
10-
pre:
11-
- npm install -g eslint
12-
- pip install tox
13-
- npm install
14-
- node_modules/.bin/builder run build-dist
15-
- node_modules/.bin/builder run copy-lib
16-
10+
override:
11+
- pip install tox
12+
- npm install -g eslint
13+
- npm install --ignore-scripts
14+
- node_modules/.bin/builder run build-dist
15+
- node_modules/.bin/builder run copy-lib
1716
test:
1817
override:
1918
- tox
20-
- npm run test
19+
- npm test
Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
'use strict';
22

3-
var partial = require('webpack-partial').default;
4-
var SourceMapDevToolPlugin = require('webpack').SourceMapDevToolPlugin;
5-
63
module.exports = function (config) {
7-
return partial(config, {
8-
plugins: [
9-
new SourceMapDevToolPlugin({
10-
append: '\n//# sourceMappingURL=http://127.0.0.1:8080/build/[url]',
11-
filename: '[file].map',
12-
test: /\.(css|js)($|\?)/
13-
})
14-
]
15-
});
4+
config.devtool = 'inline-source-map';
5+
return config;
166
};

dash_core_components/bundle.js

Lines changed: 41 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dash_core_components/metadata.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,73 @@
139139
}
140140
}
141141
},
142+
"src/components/Confirm.react.js": {
143+
"description": "Confirm wraps window.confirm",
144+
"displayName": "Confirm",
145+
"methods": [],
146+
"props": {
147+
"id": {
148+
"type": {
149+
"name": "string"
150+
},
151+
"required": false,
152+
"description": ""
153+
},
154+
"message": {
155+
"type": {
156+
"name": "string"
157+
},
158+
"required": false,
159+
"description": ""
160+
},
161+
"init": {
162+
"type": {
163+
"name": "shape",
164+
"value": {
165+
"value": {
166+
"name": "any",
167+
"required": false
168+
},
169+
"ask": {
170+
"name": "bool",
171+
"required": false
172+
}
173+
}
174+
},
175+
"required": false,
176+
"description": ""
177+
},
178+
"result": {
179+
"type": {
180+
"name": "shape",
181+
"value": {
182+
"timestamp": {
183+
"name": "custom",
184+
"raw": "PropTypes.integer",
185+
"required": false
186+
},
187+
"value": {
188+
"name": "any",
189+
"required": false
190+
}
191+
}
192+
},
193+
"required": false,
194+
"description": "",
195+
"defaultValue": {
196+
"value": "{\n timestamp: -1\n}",
197+
"computed": false
198+
}
199+
},
200+
"setProps": {
201+
"type": {
202+
"name": "func"
203+
},
204+
"required": false,
205+
"description": "Dash-assigned callback that gets fired when the value changes."
206+
}
207+
}
208+
},
142209
"src/components/DatePickerRange.react.js": {
143210
"description": "DatePickerRange is a tailor made component designed for selecting\ntimespan across multiple days off of a calendar.\n\nThe DatePicker integrates well with the Python datetime module with the\nstartDate and endDate being returned in a string format suitable for\ncreating datetime objects.\n\nThis component is based off of Airbnb's react-dates react component\nwhich can be found here: https://github.com/airbnb/react-dates",
144211
"displayName": "DatePickerRange",
@@ -1354,6 +1421,13 @@
13541421
"required": false,
13551422
"description": "The element should be automatically focused after the page loaded."
13561423
},
1424+
"disabled": {
1425+
"type": {
1426+
"name": "bool"
1427+
},
1428+
"required": false,
1429+
"description": "If true, the input is disabled and can't be clicked on."
1430+
},
13571431
"inputmode": {
13581432
"type": {
13591433
"name": "enum",

dash_core_components/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.22.1'
1+
__version__ = '0.22.2'

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-core-components",
3-
"version": "0.22.1",
3+
"version": "0.22.2",
44
"description": "Core component suite for Dash",
55
"repository": {
66
"type": "git",

src/components/Input.react.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ Input.propTypes = {
9191
*/
9292
autofocus: PropTypes.string,
9393

94+
/**
95+
* If true, the input is disabled and can't be clicked on.
96+
*/
97+
disabled: PropTypes.bool,
98+
9499
inputmode: PropTypes.oneOf([
95100
/**
96101
* Alphanumeric, non-prose content such as usernames and passwords.

test/IntegrationTests.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
from __future__ import absolute_import
2+
import os
23
import multiprocessing
34
import time
45
import unittest
56
import percy
67
from selenium import webdriver
7-
8+
from selenium.webdriver.chrome.options import Options
89

910
class IntegrationTests(unittest.TestCase):
1011

1112
@classmethod
1213
def setUpClass(cls):
1314
super(IntegrationTests, cls).setUpClass()
1415

15-
cls.driver = webdriver.Chrome()
16+
options = Options()
17+
18+
if 'DASH_TEST_CHROMEPATH' in os.environ:
19+
options.binary_location = os.environ['DASH_TEST_CHROMEPATH']
20+
21+
cls.driver = webdriver.Chrome(chrome_options=options)
1622
loader = percy.ResourceLoader(webdriver=cls.driver)
1723
cls.percy_runner = percy.Runner(loader=loader)
1824
cls.percy_runner.initialize_build()
@@ -32,13 +38,18 @@ def tearDown(self):
3238
time.sleep(3)
3339

3440
def startServer(self, app):
41+
if 'DASH_TEST_PROCESSES' in os.environ:
42+
processes = int(os.environ['DASH_TEST_PROCESSES'])
43+
else:
44+
processes = 4
45+
3546
def run():
3647
app.scripts.config.serve_locally = True
3748
app.css.config.serve_locally = True
3849
app.run_server(
3950
port=8050,
4051
debug=False,
41-
processes=4
52+
processes=processes
4253
)
4354

4455
# Run on a separate process so that it doesn't block

test/test_integration.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import dash_table_experiments as dt
1515
from selenium import webdriver
1616
from selenium.webdriver.common.keys import Keys
17+
from selenium.common.exceptions import InvalidElementStateException
1718
import time
1819
from textwrap import dedent
1920
try:
@@ -38,24 +39,28 @@ def setUp(self):
3839

3940
def wait_for_element_by_css_selector(self, selector):
4041
start_time = time.time()
42+
error = None
4143
while time.time() < start_time + 20:
4244
try:
4345
return self.driver.find_element_by_css_selector(selector)
4446
except Exception as e:
47+
error = e
4548
pass
4649
time.sleep(0.25)
47-
raise e
50+
raise error
4851

4952
def wait_for_text_to_equal(self, selector, assertion_text):
5053
start_time = time.time()
54+
error = None
5155
while time.time() < start_time + 20:
5256
el = self.wait_for_element_by_css_selector(selector)
5357
try:
5458
return self.assertEqual(el.text, assertion_text)
5559
except Exception as e:
60+
error = e
5661
pass
5762
time.sleep(0.25)
58-
raise e
63+
raise error
5964

6065
def snapshot(self, name):
6166
if 'PERCY_PROJECT' in os.environ and 'PERCY_TOKEN' in os.environ:
@@ -262,7 +267,11 @@ def test_gallery(self):
262267
),
263268

264269
html.Label('Text Input'),
265-
dcc.Input(value='MTL', type='text'),
270+
dcc.Input(value='', placeholder='type here', type='text',
271+
id='textinput'),
272+
html.Label('Disabled Text Input'),
273+
dcc.Input(value='disabled', type='text',
274+
id='disabled-textinput', disabled=True),
266275

267276
html.Label('Slider'),
268277
dcc.Slider(
@@ -340,6 +349,24 @@ def test_gallery(self):
340349
).send_keys(u'北')
341350
self.snapshot('gallery - chinese character')
342351

352+
text_input = self.driver.find_element_by_id('textinput')
353+
disabled_text_input = self.driver.find_element_by_id(
354+
'disabled-textinput')
355+
text_input.send_keys('HODOR')
356+
357+
# It seems selenium errors when send(ing)_keys on a disabled element.
358+
# In case this changes we try anyway and catch the particular
359+
# exception. In any case Percy will snapshot the disabled input style
360+
# so we are not totally dependent on the send_keys behaviour for
361+
# testing disabled state.
362+
try:
363+
disabled_text_input.send_keys('RODOH')
364+
except InvalidElementStateException:
365+
pass
366+
367+
self.snapshot('gallery - text input')
368+
369+
343370
def test_location_link(self):
344371
app = dash.Dash(__name__)
345372

@@ -360,7 +387,8 @@ def test_location_link(self):
360387
id='test-link-search',
361388
href='?testQuery=testValue',
362389
refresh=False),
363-
html.Button('I am a magic button that updates pathname', id='test-button'),
390+
html.Button('I am a magic button that updates pathname',
391+
id='test-button'),
364392
html.A('link to click', href='/test/pathname/a', id='test-a'),
365393
html.A('link to click', href='#test-hash', id='test-a-hash'),
366394
html.A('link to click', href='?queryA=valueA', id='test-a-query'),
@@ -370,13 +398,15 @@ def test_location_link(self):
370398
])
371399

372400
@app.callback(
373-
output=Output(component_id='test-pathname', component_property='children'),
401+
output=Output(component_id='test-pathname',
402+
component_property='children'),
374403
inputs=[Input(component_id='test-location', component_property='pathname')])
375404
def update_location_on_page(pathname):
376405
return pathname
377406

378407
@app.callback(
379-
output=Output(component_id='test-hash', component_property='children'),
408+
output=Output(component_id='test-hash',
409+
component_property='children'),
380410
inputs=[Input(component_id='test-location', component_property='hash')])
381411
def update_location_on_page(hash_val):
382412
if hash_val is None:
@@ -385,7 +415,8 @@ def update_location_on_page(hash_val):
385415
return hash_val
386416

387417
@app.callback(
388-
output=Output(component_id='test-search', component_property='children'),
418+
output=Output(component_id='test-search',
419+
component_property='children'),
389420
inputs=[Input(component_id='test-location', component_property='search')])
390421
def update_location_on_page(search):
391422
if search is None:
@@ -394,8 +425,10 @@ def update_location_on_page(search):
394425
return search
395426

396427
@app.callback(
397-
output=Output(component_id='test-location', component_property='pathname'),
398-
inputs=[Input(component_id='test-button', component_property='n_clicks')],
428+
output=Output(component_id='test-location',
429+
component_property='pathname'),
430+
inputs=[Input(component_id='test-button',
431+
component_property='n_clicks')],
399432
state=[State(component_id='test-location', component_property='pathname')])
400433
def update_pathname(n_clicks, current_pathname):
401434
if n_clicks is not None:
@@ -461,7 +494,6 @@ def update_pathname(n_clicks, current_pathname):
461494
self.wait_for_text_to_equal('#test-hash', '')
462495
self.snapshot('link -- /test/pathname/a?queryA=valueA')
463496

464-
465497
def test_candlestick(self):
466498
app = dash.Dash(__name__)
467499
app.layout = html.Div([

0 commit comments

Comments
 (0)