From 38a44764765f60a21194fffbafb84a2f826be306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Jan 2020 12:54:10 -0500 Subject: [PATCH 01/21] use mimetypes --- dash/dash.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index d21371bbbb..e9a1f866a1 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -12,6 +12,7 @@ import re import logging import pprint +import mimetypes from functools import wraps from textwrap import dedent @@ -39,6 +40,8 @@ from ._configs import get_combined_config, pathname_configs from .version import __version__ +mimetypes.add_type('application/json', '.map', True) + _default_index = """ @@ -703,13 +706,10 @@ def serve_component_suites(self, package_name, path_in_package_dist): ) ) - mimetype = ( - { - "js": "application/javascript", - "css": "text/css", - "map": "application/json", - } - )[path_in_package_dist.split(".")[-1]] + resource_path = os.path.dirname(sys.modules[package_name].__file__) + full_path = os.path.join(resource_path, path_in_package_dist) + + (mimetype, encoding) = mimetypes.guess_type(full_path) package = sys.modules[package_name] self.logger.debug( From 079de9171e954c07f8bab7ac552679893a577419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Jan 2020 13:03:51 -0500 Subject: [PATCH 02/21] lint --- dash/dash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index e9a1f866a1..2648af1c2f 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -709,7 +709,7 @@ def serve_component_suites(self, package_name, path_in_package_dist): resource_path = os.path.dirname(sys.modules[package_name].__file__) full_path = os.path.join(resource_path, path_in_package_dist) - (mimetype, encoding) = mimetypes.guess_type(full_path) + (mimetype, ) = mimetypes.guess_type(full_path) package = sys.modules[package_name] self.logger.debug( From b74f49fe20e18881b12d54a8fe608e1e34bb7957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Jan 2020 13:31:42 -0500 Subject: [PATCH 03/21] lint --- dash/dash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index 2648af1c2f..cea1ad5019 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -709,7 +709,7 @@ def serve_component_suites(self, package_name, path_in_package_dist): resource_path = os.path.dirname(sys.modules[package_name].__file__) full_path = os.path.join(resource_path, path_in_package_dist) - (mimetype, ) = mimetypes.guess_type(full_path) + (mimetype, _) = mimetypes.guess_type(full_path) package = sys.modules[package_name] self.logger.debug( From 8d8669a33ee329da37be1704702c7038fdc7e27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Jan 2020 13:38:08 -0500 Subject: [PATCH 04/21] comment, typo --- CHANGELOG.md | 2 +- dash/dash.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc726bc16..9a204ce719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed - [#1035](https://github.com/plotly/dash/pull/1035) Simplify our build process. -- [#1074](https://github.com/plotly/dash/pull/1045) Error messages when providing an incorrect property to a component have been improved: they now specify the component type, library, version, and ID (if available). +- [#1074](https://github.com/plotly/dash/pull/1074) Error messages when providing an incorrect property to a component have been improved: they now specify the component type, library, version, and ID (if available). ### Fixed - [#1037](https://github.com/plotly/dash/pull/1037) Fix no_update test to allow copies, such as those stored and retrieved from a cache. diff --git a/dash/dash.py b/dash/dash.py index cea1ad5019..b46814c948 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -40,6 +40,7 @@ from ._configs import get_combined_config, pathname_configs from .version import __version__ +# Add explicit mapping for map files mimetypes.add_type('application/json', '.map', True) _default_index = """ From 68af79a39c814fd56003f2e26f72dd59247e964c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 10 Jan 2020 12:24:45 -0500 Subject: [PATCH 05/21] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a204ce719..fcf25c5d28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Added +- [#1078](https://github.com/plotly/dash/pull/1078) Add support for arbitrary file extensions in component packages + ### Changed - [#1035](https://github.com/plotly/dash/pull/1035) Simplify our build process. - [#1074](https://github.com/plotly/dash/pull/1074) Error messages when providing an incorrect property to a component have been improved: they now specify the component type, library, version, and ID (if available). From ba10c38a48a45fbe1780afed06838ed70a65d589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 10 Jan 2020 12:54:01 -0500 Subject: [PATCH 06/21] simplify mimetype check --- dash/dash.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index b46814c948..66e550cecb 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -710,7 +710,8 @@ def serve_component_suites(self, package_name, path_in_package_dist): resource_path = os.path.dirname(sys.modules[package_name].__file__) full_path = os.path.join(resource_path, path_in_package_dist) - (mimetype, _) = mimetypes.guess_type(full_path) + extension = '.' + path_in_package_dist.split(".")[-1] + mimetype= mimetypes.types_map[extension] if hasattr(mimetypes.types_map, extension) else 'application/octet-stream' package = sys.modules[package_name] self.logger.debug( From 7c862f750bae4b0ccf143e78534152f46f6764e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 10 Jan 2020 12:56:33 -0500 Subject: [PATCH 07/21] remove dead code --- dash/dash.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index 66e550cecb..c628a3fb53 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -707,9 +707,6 @@ def serve_component_suites(self, package_name, path_in_package_dist): ) ) - resource_path = os.path.dirname(sys.modules[package_name].__file__) - full_path = os.path.join(resource_path, path_in_package_dist) - extension = '.' + path_in_package_dist.split(".")[-1] mimetype= mimetypes.types_map[extension] if hasattr(mimetypes.types_map, extension) else 'application/octet-stream' From bd70c7d611d615a59e4a3a8bac8ef8345a742e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 10 Jan 2020 14:15:29 -0500 Subject: [PATCH 08/21] black --- dash/dash.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index c628a3fb53..277b60d1d0 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -643,8 +643,8 @@ def _generate_scripts_html(self): if isinstance(src, dict) else ''.format(src) for src in srcs - ] + - [ + ] + + [ ''.format(src) for src in self._inline_scripts ] @@ -708,7 +708,11 @@ def serve_component_suites(self, package_name, path_in_package_dist): ) extension = '.' + path_in_package_dist.split(".")[-1] - mimetype= mimetypes.types_map[extension] if hasattr(mimetypes.types_map, extension) else 'application/octet-stream' + mimetype = ( + mimetypes.types_map[extension] + if hasattr(mimetypes.types_map, extension) + else 'application/octet-stream' + ) package = sys.modules[package_name] self.logger.debug( @@ -1275,9 +1279,11 @@ def clientside_callback( var clientside = window.dash_clientside = window.dash_clientside || {{}}; var ns = clientside["{0}"] = clientside["{0}"] || {{}}; ns["{1}"] = {2}; - """.format(namespace.replace('"', '\\"'), - function_name.replace('"', '\\"'), - clientside_function) + """.format( + namespace.replace('"', '\\"'), + function_name.replace('"', '\\"'), + clientside_function, + ) ) # Callback is stored in an external asset. From 9f3cedea5dbbac907a6b98be9ca07a2621ae4250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Thu, 26 Mar 2020 14:29:45 -0400 Subject: [PATCH 09/21] Update CHANGELOG.md Co-Authored-By: Ryan Patrick Kyle --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b95b7fc2eb..5a3e000b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [1.8.0] - 2020-01-14 ### Added -- [#1078](https://github.com/plotly/dash/pull/1078) Add support for arbitrary file extensions in component packages +- [#1078](https://github.com/plotly/dash/pull/1078) Permit usage of arbitrary file extensions for assets within component libraries - [#1073](https://github.com/plotly/dash/pull/1073) Two new functions to simplify usage handling URLs and pathnames: `app.get_relative_path` & `app.trim_relative_path`. These functions are particularly useful for apps deployed on Dash Enterprise where the apps served under a URL prefix (the app name) which is unlike apps served on localhost:8050. - `app.get_relative_path` returns a path with the config setting `requests_pathname_prefix` prefixed. Use `app.get_relative_path` anywhere you would provide a relative pathname, like `dcc.Link(href=app.relative_path('/page-2'))` or even as an alternative to `app.get_asset_url` with e.g. `html.Img(src=app.get_relative_path('/assets/logo.png'))`. From 6719aa671f4703964ef6ebc81f3bb5f7ba3de4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Apr 2020 08:53:54 -0400 Subject: [PATCH 10/21] update variable name --- dash/dash.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index 7a32888a98..fd81592f8f 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -47,7 +47,7 @@ from . import _watch # Add explicit mapping for map files -mimetypes.add_type('application/json', '.map', True) +mimetypes.add_type("application/json", ".map", True) _default_index = """ @@ -639,11 +639,11 @@ def serve_component_suites(self, package_name, fingerprinted_path): _validate.validate_js_path(self.registered_paths, package_name, path_in_pkg) - extension = '.' + path_in_package_dist.split(".")[-1] + extension = "." + path_in_pkg.split(".")[-1] mimetype = ( mimetypes.types_map[extension] if hasattr(mimetypes.types_map, extension) - else 'application/octet-stream' + else "application/octet-stream" ) package = sys.modules[package_name] From 9d8681ff0e3baa2b94063551ba5cc19221d2262e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Apr 2020 10:02:05 -0400 Subject: [PATCH 11/21] clean up mimetype logic --- dash/dash.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index fd81592f8f..1f423a3229 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -640,11 +640,7 @@ def serve_component_suites(self, package_name, fingerprinted_path): _validate.validate_js_path(self.registered_paths, package_name, path_in_pkg) extension = "." + path_in_pkg.split(".")[-1] - mimetype = ( - mimetypes.types_map[extension] - if hasattr(mimetypes.types_map, extension) - else "application/octet-stream" - ) + mimetype = mimetypes.types_map.get(extension, "application/octet-stream") package = sys.modules[package_name] self.logger.debug( From 0eacb138f83916cb950f4f750c8f98ac1d316fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Apr 2020 10:05:08 -0400 Subject: [PATCH 12/21] this feature is unreleased.. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c086487f..526594b9a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - [#1103](https://github.com/plotly/dash/pull/1103) Wildcard IDs and callbacks. Component IDs can be dictionaries, and callbacks can reference patterns of components, using three different wildcards: `ALL`, `MATCH`, and `ALLSMALLER`, available from `dash.dependencies`. This lets you create components on demand, and have callbacks respond to any and all of them. To help with this, `dash.callback_context` gets three new entries: `outputs_list`, `inputs_list`, and `states_list`, which contain all the ids, properties, and except for the outputs, the property values from all matched components. - [#1103](https://github.com/plotly/dash/pull/1103) `dash.testing` option `--pause`: after opening the dash app in a test, will invoke `pdb` for live debugging of both Javascript and Python. Use with a single test case like `pytest -k cbwc001 --pause`. +- [#1078](https://github.com/plotly/dash/pull/1078) Permit usage of arbitrary file extensions for assets within component libraries ### Changed - [#1103](https://github.com/plotly/dash/pull/1103) Multiple changes to the callback pipeline: @@ -36,7 +37,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [1.8.0] - 2020-01-14 ### Added -- [#1078](https://github.com/plotly/dash/pull/1078) Permit usage of arbitrary file extensions for assets within component libraries - [#1073](https://github.com/plotly/dash/pull/1073) Two new functions to simplify usage handling URLs and pathnames: `app.get_relative_path` & `app.trim_relative_path`. These functions are particularly useful for apps deployed on Dash Enterprise where the apps served under a URL prefix (the app name) which is unlike apps served on localhost:8050. - `app.get_relative_path` returns a path with the config setting `requests_pathname_prefix` prefixed. Use `app.get_relative_path` anywhere you would provide a relative pathname, like `dcc.Link(href=app.relative_path('/page-2'))` or even as an alternative to `app.get_asset_url` with e.g. `html.Img(src=app.get_relative_path('/assets/logo.png'))`. From cde454de3c661ba2e910818984a568b0a82669db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Apr 2020 10:28:03 -0400 Subject: [PATCH 13/21] From d606d25611ec78684106f9eef89c7ccef0a960ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 9 Apr 2020 10:48:16 -0400 Subject: [PATCH 14/21] noise From 0b0292cda74f2e659db8664e44bf7a4362e68801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 16:25:45 -0400 Subject: [PATCH 15/21] Accept all resources except py generation artifacts --- dash/development/_r_components_generation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/development/_r_components_generation.py b/dash/development/_r_components_generation.py index 7ed2d52f88..577cc914e3 100644 --- a/dash/development/_r_components_generation.py +++ b/dash/development/_r_components_generation.py @@ -505,7 +505,7 @@ def write_js_metadata(pkg_data, project_shortname, has_wildcards): for filename in filenames: extension = os.path.splitext(filename)[1] - if extension not in [".css", ".js", ".map"]: + if extension in [".py", ".pyc", ".json"]: continue target_dirname = os.path.join( From 8cad783635873a65bc09cec3153b499715d8a1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 16:41:09 -0400 Subject: [PATCH 16/21] Test arbitrary resource (ttf) on standard component --- .../base/__init__.py | 14 ++++++++++++++ .../base/godfather.ttf | Bin 0 -> 13328 bytes .../base/style.css | 4 ++++ .../src/components/MyStandardComponent.js | 7 ++++++- tests/integration/test_generation.py | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 @plotly/dash-generator-test-component-standard/base/godfather.ttf create mode 100644 @plotly/dash-generator-test-component-standard/base/style.css diff --git a/@plotly/dash-generator-test-component-standard/base/__init__.py b/@plotly/dash-generator-test-component-standard/base/__init__.py index c72087cde2..9b3b0ee30c 100644 --- a/@plotly/dash-generator-test-component-standard/base/__init__.py +++ b/@plotly/dash-generator-test-component-standard/base/__init__.py @@ -16,8 +16,22 @@ dict( relative_package_path='dash_generator_test_component_standard.js', namespace='dash_generator_test_component_standard' + ), + dict( + relative_package_path='godfather.ttf', + namespace='dash_generator_test_component_standard', + dynamic=True + ) +] + +_css_dist = [ + dict( + relative_package_path='style.css', + namespace='dash_generator_test_component_standard', + dynamic=True ) ] for _component in __all__: setattr(locals()[_component], '_js_dist', _js_dist) + setattr(locals()[_component], '_css_dist', _css_dist) diff --git a/@plotly/dash-generator-test-component-standard/base/godfather.ttf b/@plotly/dash-generator-test-component-standard/base/godfather.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a97301daa0f919cd952b6b7a9c1c5f1617df32fa GIT binary patch literal 13328 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 (
{value}
); +const MyStandardComponent = ({ id, style, value }) => (
{value}
); MyStandardComponent.propTypes = { /** @@ -12,6 +12,11 @@ MyStandardComponent.propTypes = { */ id: PropTypes.string, + /** + * The style + */ + style: PropTypes.shape, + /** * The value to display */ diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index fe1fbf7ad1..d3b6daf299 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -18,3 +18,21 @@ def test_gene001_simple_callback(dash_duo): assert dash_duo.wait_for_element("#standard").text == "Standard" assert dash_duo.wait_for_element("#nested").text == "Nested" + + +def test_gene002_arbitrary_resources(dash_duo): + app = Dash(__name__) + + app.layout = Div( + [ + MyStandardComponent( + id="standard", value="Standard", style={"font-family": "godfather"} + ), + MyNestedComponent(id="nested", value="Nested"), + ] + ) + + dash_duo.start_server(app) + + assert dash_duo.wait_for_element("#standard").text == "Standard" + assert dash_duo.wait_for_element("#nested").text == "Nested" From c1c1d3ae34ac6b07727ad686061a5354907ac0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 16:51:45 -0400 Subject: [PATCH 17/21] take snapshots --- tests/integration/test_generation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index d3b6daf299..a847436a75 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -19,6 +19,8 @@ def test_gene001_simple_callback(dash_duo): assert dash_duo.wait_for_element("#standard").text == "Standard" assert dash_duo.wait_for_element("#nested").text == "Nested" + dash_duo.percy_snapshot(name="nested-and-standard-components") + def test_gene002_arbitrary_resources(dash_duo): app = Dash(__name__) @@ -27,12 +29,12 @@ def test_gene002_arbitrary_resources(dash_duo): [ MyStandardComponent( id="standard", value="Standard", style={"font-family": "godfather"} - ), - MyNestedComponent(id="nested", value="Nested"), + ) ] ) dash_duo.start_server(app) assert dash_duo.wait_for_element("#standard").text == "Standard" - assert dash_duo.wait_for_element("#nested").text == "Nested" + + dash_duo.percy_snapshot(name="standard-godfather") From 7676a90544db6bee7501c1a212704a735526f1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 16:56:56 -0400 Subject: [PATCH 18/21] rename snapshots --- tests/integration/test_generation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index a847436a75..54c365daa3 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -19,7 +19,7 @@ def test_gene001_simple_callback(dash_duo): assert dash_duo.wait_for_element("#standard").text == "Standard" assert dash_duo.wait_for_element("#nested").text == "Nested" - dash_duo.percy_snapshot(name="nested-and-standard-components") + dash_duo.percy_snapshot(name="gene001-simple-callback") def test_gene002_arbitrary_resources(dash_duo): @@ -37,4 +37,4 @@ def test_gene002_arbitrary_resources(dash_duo): assert dash_duo.wait_for_element("#standard").text == "Standard" - dash_duo.percy_snapshot(name="standard-godfather") + dash_duo.percy_snapshot(name="gene002-arbitrary-resource") From 407d9577dd95ccd2937b5c331463db7d689fc62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 17:04:39 -0400 Subject: [PATCH 19/21] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d02351fe59..b7a012bff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [UNRELEASED] ### Added - [#1201](https://github.com/plotly/dash/pull/1201) New attribute `app.validation_layout` allows you to create a multi-page app without `suppress_callback_exceptions=True` or layout function tricks. Set this to a component layout containing the superset of all IDs on all pages in your app. +- [#1078](https://github.com/plotly/dash/pull/1078) Permit usage of arbitrary file extensions for assets within component libraries ### Fixed - [#1201](https://github.com/plotly/dash/pull/1201) Fixes [#1193](https://github.com/plotly/dash/issues/1193) - prior to Dash 1.11, you could use `flask.has_request_context() == False` inside an `app.layout` function to provide a special layout containing all IDs for validation purposes in a multi-page app. Dash 1.11 broke this when we moved most of this validation into the renderer. This change makes it work again. @@ -13,7 +14,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - [#1103](https://github.com/plotly/dash/pull/1103) Pattern-matching IDs and callbacks. Component IDs can be dictionaries, and callbacks can reference patterns of components, using three different wildcards: `ALL`, `MATCH`, and `ALLSMALLER`, available from `dash.dependencies`. This lets you create components on demand, and have callbacks respond to any and all of them. To help with this, `dash.callback_context` gets three new entries: `outputs_list`, `inputs_list`, and `states_list`, which contain all the ids, properties, and except for the outputs, the property values from all matched components. - [#1103](https://github.com/plotly/dash/pull/1103) `dash.testing` option `--pause`: after opening the dash app in a test, will invoke `pdb` for live debugging of both Javascript and Python. Use with a single test case like `pytest -k cbwc001 --pause`. -- [#1078](https://github.com/plotly/dash/pull/1078) Permit usage of arbitrary file extensions for assets within component libraries ### Changed - [#1103](https://github.com/plotly/dash/pull/1103) Multiple changes to the callback pipeline: From 18419e413b947a66764d7f539c6dc37df8d1ed79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 17:47:13 -0400 Subject: [PATCH 20/21] wait for font to be loaded --- .../base/__init__.py | 3 +-- tests/integration/test_generation.py | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/@plotly/dash-generator-test-component-standard/base/__init__.py b/@plotly/dash-generator-test-component-standard/base/__init__.py index 9b3b0ee30c..fb1bae5541 100644 --- a/@plotly/dash-generator-test-component-standard/base/__init__.py +++ b/@plotly/dash-generator-test-component-standard/base/__init__.py @@ -27,8 +27,7 @@ _css_dist = [ dict( relative_package_path='style.css', - namespace='dash_generator_test_component_standard', - dynamic=True + namespace='dash_generator_test_component_standard' ) ] diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index 54c365daa3..337b344c12 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -3,6 +3,8 @@ from dash_generator_test_component_standard import MyStandardComponent from dash_html_components import Div +from selenium.webdriver.support.ui import WebDriverWait + def test_gene001_simple_callback(dash_duo): app = Dash(__name__) @@ -37,4 +39,11 @@ def test_gene002_arbitrary_resources(dash_duo): assert dash_duo.wait_for_element("#standard").text == "Standard" + WebDriverWait(dash_duo.driver, 10).until( + lambda _: dash_duo.driver.execute_script( + "return document.fonts.check('1em godfather')" + ) + is True, + ) + dash_duo.percy_snapshot(name="gene002-arbitrary-resource") From 5bc8146c2432753e652377f67ec124e47e3d6ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 23 Apr 2020 18:24:11 -0400 Subject: [PATCH 21/21] improve test --- tests/integration/test_generation.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index 337b344c12..518c7a0af7 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -1,7 +1,10 @@ from dash import Dash +from dash.dependencies import Input, Output +from dash.exceptions import PreventUpdate + from dash_generator_test_component_nested import MyNestedComponent from dash_generator_test_component_standard import MyStandardComponent -from dash_html_components import Div +from dash_html_components import Button, Div from selenium.webdriver.support.ui import WebDriverWait @@ -27,16 +30,25 @@ def test_gene001_simple_callback(dash_duo): def test_gene002_arbitrary_resources(dash_duo): app = Dash(__name__) - app.layout = Div( - [ - MyStandardComponent( - id="standard", value="Standard", style={"font-family": "godfather"} - ) - ] - ) + app.layout = Div([Button(id="btn"), Div(id="container")]) + + @app.callback(Output("container", "children"), [Input("btn", "n_clicks")]) + def update_container(n_clicks): + if n_clicks is None: + raise PreventUpdate + + return MyStandardComponent( + id="standard", value="Standard", style={"font-family": "godfather"} + ) dash_duo.start_server(app) + assert ( + dash_duo.driver.execute_script("return document.fonts.check('1em godfather')") + is False + ) + + dash_duo.wait_for_element("#btn").click() assert dash_duo.wait_for_element("#standard").text == "Standard" WebDriverWait(dash_duo.driver, 10).until(