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

Commit

Permalink
Merge pull request #185 from plotly/addMissingInputDisabledAttr
Browse files Browse the repository at this point in the history
Add missing input disabled attr
  • Loading branch information
bpostlethwaite authored May 22, 2018
2 parents 86bc66f + 0be4e4c commit 1b713d4
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 77 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.22.2] - 2018-05-22
### Fixed
- `dcc.Input` component now handles `disabled=False` property.
- Broken sourcemaps for debugging.
### Added
- Testing configuration for CHROMEPATH and SERVER_PROCESSES

## [0.22.1] - 2018-04-09
### Fixed
- Various bugs with the `ohlc` and `candlestick` chart type in the `dcc.Graph`
Expand Down
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,16 @@ like this:
```
export TOX_PYTHON_27=~/.pyenv/versions/2.7.14/bin/python
```

## Local configuration
You can configure the test server with the following variables:
### DASH_TEST_CHROMEPATH
If you run a special chrome set the path to your chrome binary with this environment variable.

### DASH_TEST_PROCESSES
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.

### Example: single test run with configuration
```
DASH_TEST_CHROMEPATH=/bin/google-chrome-beta DASH_TEST_PROCESSES=1 python -m unittest -v test.test_integration.Tests.test_inputs
```
15 changes: 7 additions & 8 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ machine:
TOX_PYTHON_27: python2.7

dependencies:
pre:
- npm install -g eslint
- pip install tox
- npm install
- node_modules/.bin/builder run build-dist
- node_modules/.bin/builder run copy-lib

override:
- pip install tox
- npm install -g eslint
- npm install --ignore-scripts
- node_modules/.bin/builder run build-dist
- node_modules/.bin/builder run copy-lib
test:
override:
- tox
- npm run test
- npm test
14 changes: 2 additions & 12 deletions config/webpack/partials/sourceMapDev.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
'use strict';

var partial = require('webpack-partial').default;
var SourceMapDevToolPlugin = require('webpack').SourceMapDevToolPlugin;

module.exports = function (config) {
return partial(config, {
plugins: [
new SourceMapDevToolPlugin({
append: '\n//# sourceMappingURL=http://127.0.0.1:8080/build/[url]',
filename: '[file].map',
test: /\.(css|js)($|\?)/
})
]
});
config.devtool = 'inline-source-map';
return config;
};
83 changes: 41 additions & 42 deletions dash_core_components/bundle.js

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions dash_core_components/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,73 @@
}
}
},
"src/components/Confirm.react.js": {
"description": "Confirm wraps window.confirm",
"displayName": "Confirm",
"methods": [],
"props": {
"id": {
"type": {
"name": "string"
},
"required": false,
"description": ""
},
"message": {
"type": {
"name": "string"
},
"required": false,
"description": ""
},
"init": {
"type": {
"name": "shape",
"value": {
"value": {
"name": "any",
"required": false
},
"ask": {
"name": "bool",
"required": false
}
}
},
"required": false,
"description": ""
},
"result": {
"type": {
"name": "shape",
"value": {
"timestamp": {
"name": "custom",
"raw": "PropTypes.integer",
"required": false
},
"value": {
"name": "any",
"required": false
}
}
},
"required": false,
"description": "",
"defaultValue": {
"value": "{\n timestamp: -1\n}",
"computed": false
}
},
"setProps": {
"type": {
"name": "func"
},
"required": false,
"description": "Dash-assigned callback that gets fired when the value changes."
}
}
},
"src/components/DatePickerRange.react.js": {
"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",
"displayName": "DatePickerRange",
Expand Down Expand Up @@ -1354,6 +1421,13 @@
"required": false,
"description": "The element should be automatically focused after the page loaded."
},
"disabled": {
"type": {
"name": "bool"
},
"required": false,
"description": "If true, the input is disabled and can't be clicked on."
},
"inputmode": {
"type": {
"name": "enum",
Expand Down
2 changes: 1 addition & 1 deletion dash_core_components/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.22.1'
__version__ = '0.22.2'
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dash-core-components",
"version": "0.22.1",
"version": "0.22.2",
"description": "Core component suite for Dash",
"repository": {
"type": "git",
Expand Down
5 changes: 5 additions & 0 deletions src/components/Input.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ Input.propTypes = {
*/
autofocus: PropTypes.string,

/**
* If true, the input is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,

inputmode: PropTypes.oneOf([
/**
* Alphanumeric, non-prose content such as usernames and passwords.
Expand Down
17 changes: 14 additions & 3 deletions test/IntegrationTests.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
from __future__ import absolute_import
import os
import multiprocessing
import time
import unittest
import percy
from selenium import webdriver

from selenium.webdriver.chrome.options import Options

class IntegrationTests(unittest.TestCase):

@classmethod
def setUpClass(cls):
super(IntegrationTests, cls).setUpClass()

cls.driver = webdriver.Chrome()
options = Options()

if 'DASH_TEST_CHROMEPATH' in os.environ:
options.binary_location = os.environ['DASH_TEST_CHROMEPATH']

cls.driver = webdriver.Chrome(chrome_options=options)
loader = percy.ResourceLoader(webdriver=cls.driver)
cls.percy_runner = percy.Runner(loader=loader)
cls.percy_runner.initialize_build()
Expand All @@ -32,13 +38,18 @@ def tearDown(self):
time.sleep(3)

def startServer(self, app):
if 'DASH_TEST_PROCESSES' in os.environ:
processes = int(os.environ['DASH_TEST_PROCESSES'])
else:
processes = 4

def run():
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True
app.run_server(
port=8050,
debug=False,
processes=4
processes=processes
)

# Run on a separate process so that it doesn't block
Expand Down
52 changes: 42 additions & 10 deletions test/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import dash_table_experiments as dt
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import InvalidElementStateException
import time
from textwrap import dedent
try:
Expand All @@ -38,24 +39,28 @@ def setUp(self):

def wait_for_element_by_css_selector(self, selector):
start_time = time.time()
error = None
while time.time() < start_time + 20:
try:
return self.driver.find_element_by_css_selector(selector)
except Exception as e:
error = e
pass
time.sleep(0.25)
raise e
raise error

def wait_for_text_to_equal(self, selector, assertion_text):
start_time = time.time()
error = None
while time.time() < start_time + 20:
el = self.wait_for_element_by_css_selector(selector)
try:
return self.assertEqual(el.text, assertion_text)
except Exception as e:
error = e
pass
time.sleep(0.25)
raise e
raise error

def snapshot(self, name):
if 'PERCY_PROJECT' in os.environ and 'PERCY_TOKEN' in os.environ:
Expand Down Expand Up @@ -262,7 +267,11 @@ def test_gallery(self):
),

html.Label('Text Input'),
dcc.Input(value='MTL', type='text'),
dcc.Input(value='', placeholder='type here', type='text',
id='textinput'),
html.Label('Disabled Text Input'),
dcc.Input(value='disabled', type='text',
id='disabled-textinput', disabled=True),

html.Label('Slider'),
dcc.Slider(
Expand Down Expand Up @@ -340,6 +349,24 @@ def test_gallery(self):
).send_keys(u'北')
self.snapshot('gallery - chinese character')

text_input = self.driver.find_element_by_id('textinput')
disabled_text_input = self.driver.find_element_by_id(
'disabled-textinput')
text_input.send_keys('HODOR')

# It seems selenium errors when send(ing)_keys on a disabled element.
# In case this changes we try anyway and catch the particular
# exception. In any case Percy will snapshot the disabled input style
# so we are not totally dependent on the send_keys behaviour for
# testing disabled state.
try:
disabled_text_input.send_keys('RODOH')
except InvalidElementStateException:
pass

self.snapshot('gallery - text input')


def test_location_link(self):
app = dash.Dash(__name__)

Expand All @@ -360,7 +387,8 @@ def test_location_link(self):
id='test-link-search',
href='?testQuery=testValue',
refresh=False),
html.Button('I am a magic button that updates pathname', id='test-button'),
html.Button('I am a magic button that updates pathname',
id='test-button'),
html.A('link to click', href='/test/pathname/a', id='test-a'),
html.A('link to click', href='#test-hash', id='test-a-hash'),
html.A('link to click', href='?queryA=valueA', id='test-a-query'),
Expand All @@ -370,13 +398,15 @@ def test_location_link(self):
])

@app.callback(
output=Output(component_id='test-pathname', component_property='children'),
output=Output(component_id='test-pathname',
component_property='children'),
inputs=[Input(component_id='test-location', component_property='pathname')])
def update_location_on_page(pathname):
return pathname

@app.callback(
output=Output(component_id='test-hash', component_property='children'),
output=Output(component_id='test-hash',
component_property='children'),
inputs=[Input(component_id='test-location', component_property='hash')])
def update_location_on_page(hash_val):
if hash_val is None:
Expand All @@ -385,7 +415,8 @@ def update_location_on_page(hash_val):
return hash_val

@app.callback(
output=Output(component_id='test-search', component_property='children'),
output=Output(component_id='test-search',
component_property='children'),
inputs=[Input(component_id='test-location', component_property='search')])
def update_location_on_page(search):
if search is None:
Expand All @@ -394,8 +425,10 @@ def update_location_on_page(search):
return search

@app.callback(
output=Output(component_id='test-location', component_property='pathname'),
inputs=[Input(component_id='test-button', component_property='n_clicks')],
output=Output(component_id='test-location',
component_property='pathname'),
inputs=[Input(component_id='test-button',
component_property='n_clicks')],
state=[State(component_id='test-location', component_property='pathname')])
def update_pathname(n_clicks, current_pathname):
if n_clicks is not None:
Expand Down Expand Up @@ -461,7 +494,6 @@ def update_pathname(n_clicks, current_pathname):
self.wait_for_text_to_equal('#test-hash', '')
self.snapshot('link -- /test/pathname/a?queryA=valueA')


def test_candlestick(self):
app = dash.Dash(__name__)
app.layout = html.Div([
Expand Down

0 comments on commit 1b713d4

Please sign in to comment.