Skip to content

Commit

Permalink
Merge pull request plotly#140 from plotly/fix-ie11
Browse files Browse the repository at this point in the history
Fix Link and Location for IE
  • Loading branch information
chriddyp committed Jan 8, 2018
1 parent b721680 commit 540d3f9
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 152 deletions.
13 changes: 10 additions & 3 deletions packages/dash-core-components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.15.5] - 2017-01-08
### Fixed
- The `dash_core_components.Location` and `dash_core_components.Link` properties
should now work on Internet Explorer.
Thanks to @nedned for suggesting a solution.
Fixes https://github.com/plotly/dash-core-components/pull/113

## [0.15.4] - 2017-12-21
### Changed
- The `dash_core_components.Location` component now supports `hash`,
`href`, and `search` in addition to the already supported `pathname`
(mimicking the `window.location` API). `href` can be used to handle
`pathname`, `hash`, and `search` in aggregate, or each can be manipulated
independently.
- The `children` property of `dash_core_components.Markdown` and
`dash_core_components.SyntaxHighlighter` now accepts an
- The `children` property of `dash_core_components.Markdown` and
`dash_core_components.SyntaxHighlighter` now accepts an
array of strings (previously it *had* to be a string). Now,
if an array is provided, it is collapsed into a string with line
if an array is provided, it is collapsed into a string with line
breaks (see #134).

## [0.15.3] - 2017-12-11
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.15.4'
__version__ = '0.15.5'
2 changes: 1 addition & 1 deletion packages/dash-core-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dash-core-components",
"version": "0.15.4",
"version": "0.15.5",
"description": "Core component suite for Dash",
"repository": {
"type": "git",
Expand Down
19 changes: 18 additions & 1 deletion packages/dash-core-components/src/components/Link.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

import React, {Component, PropTypes} from 'react';

/*
* event polyfill for IE
* https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
*/
function CustomEvent (event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(
event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;

export default class Link extends Component {
constructor(props) {
super(props);
Expand All @@ -14,7 +31,7 @@ export default class Link extends Component {
window.location.pathname = href;
} else {
window.history.pushState({}, '', href);
window.dispatchEvent(new Event('onpushstate'));
window.dispatchEvent(new CustomEvent('onpushstate'));
}
}

Expand Down
131 changes: 66 additions & 65 deletions packages/dash-core-components/test/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
import dash_table_experiments as dt
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse

from .IntegrationTests import IntegrationTests
from .utils import invincible, wait_for, waiter

# Download geckodriver: https://github.com/mozilla/geckodriver/releases
# And add to path:
Expand All @@ -34,21 +34,28 @@

class Tests(IntegrationTests):
def setUp(self):
def wait_for_element_by_id(id):
wait_for(lambda: None is not invincible(
lambda: self.driver.find_element_by_id(id)
))
return self.driver.find_element_by_id(id)
self.wait_for_element_by_id = wait_for_element_by_id

def wait_for_element_by_css_selector(css_selector):
wait_for(lambda: None is not invincible(
lambda: self.driver.find_element_by_css_selector(css_selector)
))
return self.driver.find_element_by_css_selector(css_selector)
self.wait_for_element_by_css_selector = (
wait_for_element_by_css_selector
)
pass

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

def wait_for_text_to_equal(self, selector, assertion_text):
start_time = time.time()
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:
pass
time.sleep(0.25)
raise e

def snapshot(self, name):
if 'PERCY_PROJECT' in os.environ and 'PERCY_TOKEN' in os.environ:
Expand Down Expand Up @@ -100,7 +107,9 @@ def update_output(contents):
df = pd.read_csv(io.StringIO(base64.b64decode(
content_string).decode('utf-8')))
return html.Div([
dt.DataTable(rows=df.to_dict('records')),
dt.DataTable(
rows=df.to_dict('records'),
columns=['city', 'country']),
html.Hr(),
html.Div('Raw Content'),
html.Pre(contents, style=pre_style)
Expand All @@ -109,7 +118,9 @@ def update_output(contents):
df = pd.read_excel(io.BytesIO(base64.b64decode(
content_string)))
return html.Div([
dt.DataTable(rows=df.to_dict('records')),
dt.DataTable(
rows=df.to_dict('records'),
columns=['city', 'country']),
html.Hr(),
html.Div('Raw Content'),
html.Pre(contents, style=pre_style)
Expand All @@ -131,10 +142,10 @@ def update_output(contents):
self.startServer(app)

try:
self.wait_for_element_by_id('waitfor')
self.wait_for_element_by_css_selector('#waitfor')
except Exception as e:
print(self.wait_for_element_by_id(
'_dash-app-content').get_attribute('innerHTML'))
print(self.wait_for_element_by_css_selector(
'#_dash-app-content').get_attribute('innerHTML'))
raise e

upload_div = self.wait_for_element_by_css_selector(
Expand Down Expand Up @@ -189,10 +200,10 @@ def test_upload_gallery(self):
self.startServer(app)

try:
self.wait_for_element_by_id('waitfor')
self.wait_for_element_by_css_selector('#waitfor')
except Exception as e:
print(self.wait_for_element_by_id(
'_dash-app-content').get_attribute('innerHTML'))
print(self.wait_for_element_by_css_selector(
'#_dash-app-content').get_attribute('innerHTML'))
raise e

self.snapshot('test_upload_gallery')
Expand Down Expand Up @@ -311,12 +322,7 @@ def test_gallery(self):
])
self.startServer(app)

try:
self.wait_for_element_by_id('waitfor')
except Exception as e:
print(self.wait_for_element_by_id(
'_dash-app-content').get_attribute('innerHTML'))
raise e
self.wait_for_element_by_css_selector('#waitfor')

self.snapshot('gallery')

Expand Down Expand Up @@ -393,60 +399,55 @@ def update_pathname(n_clicks, current_pathname):
self.snapshot('link -- location')

# Check that link updates pathname
self.driver.find_element_by_id('test-link').click()

self.snapshot('link --- /test/pathname')
self.wait_for_element_by_css_selector('#test-link').click()
self.assertEqual(
self.driver.current_url.replace('http://localhost:8050', ''),
'/test/pathname')
self.assertEqual(
self.driver.find_element_by_id('test-pathname').text,
'/test/pathname')
self.wait_for_text_to_equal('#test-pathname', '/test/pathname')

# Check that hash is updated in the Location
self.driver.find_element_by_id('test-link-hash').click()

self.wait_for_element_by_css_selector('#test-link-hash').click()
self.wait_for_text_to_equal('#test-pathname', '/test/pathname')
self.wait_for_text_to_equal('#test-hash', '#test')
self.snapshot('link -- /test/pathname#test')
self.assertEqual(self.driver.find_element_by_id('test-pathname').text, '/test/pathname')
self.assertEqual(self.driver.find_element_by_id('test-hash').text, '#test')

# Check that search is updated in the Location -- note that this goes through href and therefore wipes the hash
self.driver.find_element_by_id('test-link-search').click()

self.wait_for_element_by_css_selector('#test-link-search').click()
self.wait_for_text_to_equal('#test-search', '?testQuery=testValue')
self.wait_for_text_to_equal('#test-hash', '')
self.snapshot('link -- /test/pathname?testQuery=testValue')
self.assertEqual(self.driver.find_element_by_id('test-search').text, '?testQuery=testValue')
self.assertEqual(self.driver.find_element_by_id('test-hash').text, '')

# Check that pathname is updated through a Button click via props
self.driver.find_element_by_id('test-button').click()
time.sleep(1) # Need to wait for the callback to fire TODO is there a better way to wait?

self.wait_for_element_by_css_selector('#test-button').click()
self.wait_for_text_to_equal('#test-pathname', '/new/pathname')
self.wait_for_text_to_equal('#test-search', '?testQuery=testValue')
self.snapshot('link -- /new/pathname?testQuery=testValue')
self.assertEqual(self.driver.find_element_by_id('test-pathname').text, '/new/pathname')
self.assertEqual(self.driver.find_element_by_id('test-search').text, '?testQuery=testValue')

# Check that pathname is updated through an a tag click via props
self.driver.find_element_by_id('test-a').click()
waiter(self.wait_for_element_by_id)
self.wait_for_element_by_css_selector('#test-a').click()
try:
self.wait_for_element_by_css_selector('#waitfor')
except Exception as e:
print(self.wait_for_element_by_css_selector(
'#_dash-app-content').get_attribute('innerHTML'))
raise e

self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a')
self.wait_for_text_to_equal('#test-search', '')
self.wait_for_text_to_equal('#test-hash', '')
self.snapshot('link -- /test/pathname/a')
self.assertEqual(self.driver.find_element_by_id('test-pathname').text, '/test/pathname/a')
self.assertEqual(self.driver.find_element_by_id('test-search').text, '')
self.assertEqual(self.driver.find_element_by_id('test-hash').text, '')

# Check that hash is updated through an a tag click via props
self.driver.find_element_by_id('test-a-hash').click()

self.wait_for_element_by_css_selector('#test-a-hash').click()
self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a')
self.wait_for_text_to_equal('#test-search', '')
self.wait_for_text_to_equal('#test-hash', '#test-hash')
self.snapshot('link -- /test/pathname/a#test-hash')
self.assertEqual(self.driver.find_element_by_id('test-pathname').text, '/test/pathname/a')
self.assertEqual(self.driver.find_element_by_id('test-search').text, '')
self.assertEqual(self.driver.find_element_by_id('test-hash').text, '#test-hash')

# Check that hash is updated through an a tag click via props
self.driver.find_element_by_id('test-a-query').click()
waiter(self.wait_for_element_by_id)

self.wait_for_element_by_css_selector('#test-a-query').click()
self.wait_for_element_by_css_selector('#waitfor')
self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a')
self.wait_for_text_to_equal('#test-search', '?queryA=valueA')
self.wait_for_text_to_equal('#test-hash', '')
self.snapshot('link -- /test/pathname/a?queryA=valueA')
self.assertEqual(self.driver.find_element_by_id('test-pathname').text, '/test/pathname/a')
self.assertEqual(self.driver.find_element_by_id('test-search').text, '?queryA=valueA')
self.assertEqual(self.driver.find_element_by_id('test-hash').text, '')
81 changes: 0 additions & 81 deletions packages/dash-core-components/test/utils.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,6 @@
import time


TIMEOUT = 20 # Seconds


def invincible(func):
def wrap():
try:
return func()
except:
pass
return wrap


class WaitForTimeout(Exception):
"""This should only be raised inside the `wait_for` function."""
pass


def wait_for(condition_function, get_message=lambda: '', *args, **kwargs):
"""
Waits for condition_function to return True or raises WaitForTimeout.
:param (function) condition_function: Should return True on success.
:param args: Optional args to pass to condition_function.
:param kwargs: Optional kwargs to pass to condition_function.
if `timeout` is in kwargs, it will be used to override TIMEOUT
:raises: WaitForTimeout If condition_function doesn't return True in time.
Usage:
def get_element(selector):
# some code to get some element or return a `False`-y value.
selector = '.js-plotly-plot'
try:
wait_for(get_element, selector)
except WaitForTimeout:
self.fail('element never appeared...')
plot = get_element(selector) # we know it exists.
"""
def wrapped_condition_function():
"""We wrap this to alter the call base on the closure."""
if args and kwargs:
return condition_function(*args, **kwargs)
if args:
return condition_function(*args)
if kwargs:
return condition_function(**kwargs)
return condition_function()

if 'timeout' in kwargs:
timeout = kwargs['timeout']
del kwargs['timeout']
else:
timeout = TIMEOUT

start_time = time.time()
while time.time() < start_time + timeout:
if wrapped_condition_function():
return True
time.sleep(0.5)

raise WaitForTimeout(get_message())


def waiter(waiter_func, waitfor_string='waitfor'):
"""
Wait for an
:param waiter_func: Function that takes a string and waits
:param waitfor_string: String to wait for (an id or class name, e.g., dependeing on waiter_func).
Defaults to 'waitfor'
Usage:
def get_element(selector):
# some code to get some element or return a `False`-y value.
selector = '.js-plotly-plot'
waiter(my_waiting_function, selector) # Throws an error if the selector is not found
plot = get_element(selector) # we know it exists.
"""
time.sleep(1)
try:
waiter_func(waitfor_string)
except Exception as e:
print(waiter_func('_dash-app-content').get_attribute('innerHTML'))
raise e


def assert_clean_console(TestClass):
def assert_no_console_errors(TestClass):
TestClass.assertEqual(
Expand Down

0 comments on commit 540d3f9

Please sign in to comment.