diff --git a/CHANGELOG.md b/CHANGELOG.md
index 519e2f0fe7..1ed3850535 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.23.1 - 2018-08-02
+## Added
+- Add ie-compat meta tag to the index by default. [#316](https://github.com/plotly/dash/pull/316)
+- Add `external_script` and `external_css` keywords to dash `__init__`. [#305](https://github.com/plotly/dash/pull/305)
+
## 0.23.0 - 2018-08-01
## Added
- Dash components are now generated at build-time and then imported rather than generated when a module is imported. This should reduce the time it takes to import Dash component libraries, and makes Dash compatible with IDEs.
diff --git a/dash/_utils.py b/dash/_utils.py
index 1c6e5ca51c..bc71fde955 100644
--- a/dash/_utils.py
+++ b/dash/_utils.py
@@ -6,6 +6,20 @@ def interpolate_str(template, **data):
return s
+def format_tag(tag_name, attributes, inner='', closed=False, opened=False):
+ tag = '<{tag} {attributes}'
+ if closed:
+ tag += '/>'
+ elif opened:
+ tag += '>'
+ else:
+ tag += '>' + inner + '{tag}>'
+ return tag.format(
+ tag=tag_name,
+ attributes=' '.join([
+ '{}="{}"'.format(k, v) for k, v in attributes.items()]))
+
+
class AttributeDict(dict):
"""
Dictionary subclass enabling attribute lookup/assignment of keys/values.
diff --git a/dash/dash.py b/dash/dash.py
index a561803057..7e64b7a973 100644
--- a/dash/dash.py
+++ b/dash/dash.py
@@ -23,6 +23,7 @@
from . import exceptions
from ._utils import AttributeDict as _AttributeDict
from ._utils import interpolate_str as _interpolate
+from ._utils import format_tag as _format_tag
_default_index = '''
@@ -61,7 +62,7 @@
# pylint: disable=too-many-instance-attributes
-# pylint: disable=too-many-arguments
+# pylint: disable=too-many-arguments, too-many-locals
class Dash(object):
def __init__(
self,
@@ -75,6 +76,8 @@ def __init__(
compress=True,
meta_tags=None,
index_string=_default_index,
+ external_scripts=None,
+ external_stylesheets=None,
**kwargs):
# pylint-disable: too-many-instance-attributes
@@ -128,6 +131,10 @@ def _handle_error(error):
# static files from the packages
self.css = Css()
self.scripts = Scripts()
+
+ self._external_scripts = external_scripts or []
+ self._external_stylesheets = external_stylesheets or []
+
self.registered_paths = {}
# urls
@@ -302,9 +309,12 @@ def _relative_url_path(relative_package_path='', namespace=''):
def _generate_css_dist_html(self):
links = self._collect_and_register_resources(
self.css.get_all_css()
- )
+ ) + self._external_stylesheets
+
return '\n'.join([
- ''.format(link)
+ _format_tag('link', link, opened=True)
+ if isinstance(link, dict)
+ else ''.format(link)
for link in links
])
@@ -325,9 +335,12 @@ def _generate_scripts_html(self):
dash_renderer._js_dist
)
)
+ srcs = srcs[:-1] + self._external_scripts + [srcs[-1]]
return '\n'.join([
- ''.format(src)
+ _format_tag('script', src)
+ if isinstance(src, dict)
+ else ''.format(src)
for src in srcs
])
@@ -348,12 +361,11 @@ def _generate_meta_html(self):
if not has_ie_compat:
tags.append('')
if not has_charset:
- tags.append('')
- for meta in self._meta_tags:
- attributes = []
- for k, v in meta.items():
- attributes.append('{}="{}"'.format(k, v))
- tags.append(''.format(' '.join(attributes)))
+ tags.append('')
+
+ tags = tags + [
+ _format_tag('meta', x, opened=True) for x in self._meta_tags
+ ]
return '\n '.join(tags)
diff --git a/dash/version.py b/dash/version.py
index 08a9dbff61..db714a8550 100644
--- a/dash/version.py
+++ b/dash/version.py
@@ -1 +1 @@
-__version__ = '0.23.0'
+__version__ = '0.23.1'
diff --git a/tests/test_integration.py b/tests/test_integration.py
index b04675195b..2e8a2c75dc 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -434,4 +434,39 @@ def will_raise():
self.assertTrue('{%config%}' in exc_msg)
self.assertTrue('{%scripts%}' in exc_msg)
time.sleep(0.5)
- print('invalid index string')
+
+ def test_external_files_init(self):
+ js_files = [
+ 'https://www.google-analytics.com/analytics.js',
+ {'src': 'https://cdn.polyfill.io/v2/polyfill.min.js'},
+ {
+ 'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
+ 'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
+ 'crossorigin': 'anonymous'
+ }
+ ]
+ css_files = [
+ 'https://codepen.io/chriddyp/pen/bWLwgP.css',
+ {
+ 'href': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
+ 'rel': 'stylesheet',
+ 'integrity': 'sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO',
+ 'crossorigin': 'anonymous'
+ }
+ ]
+
+ app = dash.Dash(
+ external_scripts=js_files, external_stylesheets=css_files)
+
+ app.layout = html.Div()
+
+ self.startServer(app)
+ time.sleep(0.5)
+
+ js_urls = [x['src'] if isinstance(x, dict) else x for x in js_files]
+ css_urls = [x['href'] if isinstance(x, dict) else x for x in css_files]
+
+ for fmt, url in itertools.chain(
+ (("//script[@src='{}']", x) for x in js_urls),
+ (("//link[@href='{}']", x) for x in css_urls)):
+ self.driver.find_element_by_xpath(fmt.format(url))