Skip to content

Commit 1f27bac

Browse files
authored
Merge pull request #305 from plotly/add-js-css-init
Add `external_js/css_urls` to dash init.
2 parents e3a5486 + 4934c7f commit 1f27bac

File tree

5 files changed

+78
-12
lines changed

5 files changed

+78
-12
lines changed

Diff for: CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.23.1 - 2018-08-02
2+
## Added
3+
- Add ie-compat meta tag to the index by default. [#316](https://github.com/plotly/dash/pull/316)
4+
- Add `external_script` and `external_css` keywords to dash `__init__`. [#305](https://github.com/plotly/dash/pull/305)
5+
16
## 0.23.0 - 2018-08-01
27
## Added
38
- 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 for: dash/_utils.py

+14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ def interpolate_str(template, **data):
66
return s
77

88

9+
def format_tag(tag_name, attributes, inner='', closed=False, opened=False):
10+
tag = '<{tag} {attributes}'
11+
if closed:
12+
tag += '/>'
13+
elif opened:
14+
tag += '>'
15+
else:
16+
tag += '>' + inner + '</{tag}>'
17+
return tag.format(
18+
tag=tag_name,
19+
attributes=' '.join([
20+
'{}="{}"'.format(k, v) for k, v in attributes.items()]))
21+
22+
923
class AttributeDict(dict):
1024
"""
1125
Dictionary subclass enabling attribute lookup/assignment of keys/values.

Diff for: dash/dash.py

+22-10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from . import exceptions
2424
from ._utils import AttributeDict as _AttributeDict
2525
from ._utils import interpolate_str as _interpolate
26+
from ._utils import format_tag as _format_tag
2627

2728
_default_index = '''
2829
<!DOCTYPE html>
@@ -61,7 +62,7 @@
6162

6263

6364
# pylint: disable=too-many-instance-attributes
64-
# pylint: disable=too-many-arguments
65+
# pylint: disable=too-many-arguments, too-many-locals
6566
class Dash(object):
6667
def __init__(
6768
self,
@@ -75,6 +76,8 @@ def __init__(
7576
compress=True,
7677
meta_tags=None,
7778
index_string=_default_index,
79+
external_scripts=None,
80+
external_stylesheets=None,
7881
**kwargs):
7982

8083
# pylint-disable: too-many-instance-attributes
@@ -128,6 +131,10 @@ def _handle_error(error):
128131
# static files from the packages
129132
self.css = Css()
130133
self.scripts = Scripts()
134+
135+
self._external_scripts = external_scripts or []
136+
self._external_stylesheets = external_stylesheets or []
137+
131138
self.registered_paths = {}
132139

133140
# urls
@@ -302,9 +309,12 @@ def _relative_url_path(relative_package_path='', namespace=''):
302309
def _generate_css_dist_html(self):
303310
links = self._collect_and_register_resources(
304311
self.css.get_all_css()
305-
)
312+
) + self._external_stylesheets
313+
306314
return '\n'.join([
307-
'<link rel="stylesheet" href="{}">'.format(link)
315+
_format_tag('link', link, opened=True)
316+
if isinstance(link, dict)
317+
else '<link rel="stylesheet" href="{}">'.format(link)
308318
for link in links
309319
])
310320

@@ -325,9 +335,12 @@ def _generate_scripts_html(self):
325335
dash_renderer._js_dist
326336
)
327337
)
338+
srcs = srcs[:-1] + self._external_scripts + [srcs[-1]]
328339

329340
return '\n'.join([
330-
'<script src="{}"></script>'.format(src)
341+
_format_tag('script', src)
342+
if isinstance(src, dict)
343+
else '<script src="{}"></script>'.format(src)
331344
for src in srcs
332345
])
333346

@@ -348,12 +361,11 @@ def _generate_meta_html(self):
348361
if not has_ie_compat:
349362
tags.append('<meta equiv="X-UA-Compatible" content="IE=edge">')
350363
if not has_charset:
351-
tags.append('<meta charset="UTF-8">')
352-
for meta in self._meta_tags:
353-
attributes = []
354-
for k, v in meta.items():
355-
attributes.append('{}="{}"'.format(k, v))
356-
tags.append('<meta {}>'.format(' '.join(attributes)))
364+
tags.append('<meta charset="UTF-8"/>')
365+
366+
tags = tags + [
367+
_format_tag('meta', x, opened=True) for x in self._meta_tags
368+
]
357369

358370
return '\n '.join(tags)
359371

Diff for: dash/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.23.0'
1+
__version__ = '0.23.1'

Diff for: tests/test_integration.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,39 @@ def will_raise():
434434
self.assertTrue('{%config%}' in exc_msg)
435435
self.assertTrue('{%scripts%}' in exc_msg)
436436
time.sleep(0.5)
437-
print('invalid index string')
437+
438+
def test_external_files_init(self):
439+
js_files = [
440+
'https://www.google-analytics.com/analytics.js',
441+
{'src': 'https://cdn.polyfill.io/v2/polyfill.min.js'},
442+
{
443+
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
444+
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
445+
'crossorigin': 'anonymous'
446+
}
447+
]
448+
css_files = [
449+
'https://codepen.io/chriddyp/pen/bWLwgP.css',
450+
{
451+
'href': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
452+
'rel': 'stylesheet',
453+
'integrity': 'sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO',
454+
'crossorigin': 'anonymous'
455+
}
456+
]
457+
458+
app = dash.Dash(
459+
external_scripts=js_files, external_stylesheets=css_files)
460+
461+
app.layout = html.Div()
462+
463+
self.startServer(app)
464+
time.sleep(0.5)
465+
466+
js_urls = [x['src'] if isinstance(x, dict) else x for x in js_files]
467+
css_urls = [x['href'] if isinstance(x, dict) else x for x in css_files]
468+
469+
for fmt, url in itertools.chain(
470+
(("//script[@src='{}']", x) for x in js_urls),
471+
(("//link[@href='{}']", x) for x in css_urls)):
472+
self.driver.find_element_by_xpath(fmt.format(url))

0 commit comments

Comments
 (0)