Skip to content

Commit fb403dd

Browse files
authored
Merge branch 'dev' into add-more-tests
2 parents 234dc7b + 230d664 commit fb403dd

File tree

11 files changed

+175
-37
lines changed

11 files changed

+175
-37
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@ This project adheres to [Semantic Versioning](https://semver.org/).
55
## [UNRELEASED]
66

77
## Added
8+
9+
- [#2795](https://github.com/plotly/dash/pull/2795) Allow list of components to be passed as layout.
810
- [2760](https://github.com/plotly/dash/pull/2760) New additions to dcc.Loading resolving multiple issues:
911
- `delay_show` and `delay_hide` props to prevent flickering during brief loading periods (similar to Dash Bootstrap Components dbc.Spinner)
1012
- `overlay_style` for styling the loading overlay, such as setting visibility and opacity for children
1113
- `target_components` specifies components/props triggering the loading spinner
1214
- `custom_spinner` enables using a custom component for loading messages instead of built-in spinners
1315
- `display` overrides the loading status with options for "show," "hide," or "auto"
1416

17+
## Fixed
18+
19+
- [#2362](https://github.com/plotly/dash/pull/2362) Global namespace not polluted any more when loading clientside callbacks.
20+
1521
## [2.16.1] - 2024-03-06
1622

1723
## Fixed

components/dash-core-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,6 @@
103103
"react-dom": ">=16"
104104
},
105105
"browserslist": [
106-
"last 8 years and not dead"
106+
"last 9 years and not dead"
107107
]
108108
}

components/dash-html-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@
5959
"/dash_html_components/*{.js,.map}"
6060
],
6161
"browserslist": [
62-
"last 8 years and not dead"
62+
"last 9 years and not dead"
6363
]
6464
}

components/dash-table/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,6 @@
125125
"npm": ">=6.1.0"
126126
},
127127
"browserslist": [
128-
"last 8 years and not dead"
128+
"last 9 years and not dead"
129129
]
130130
}

dash/_callback.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,11 @@ def add_context(*args, **kwargs):
502502

503503

504504
_inline_clientside_template = """
505-
var clientside = window.dash_clientside = window.dash_clientside || {{}};
506-
var ns = clientside["{namespace}"] = clientside["{namespace}"] || {{}};
507-
ns["{function_name}"] = {clientside_function};
505+
(function() {{
506+
var clientside = window.dash_clientside = window.dash_clientside || {{}};
507+
var ns = clientside["{namespace}"] = clientside["{namespace}"] || {{}};
508+
ns["{function_name}"] = {clientside_function};
509+
}})();
508510
"""
509511

510512

dash/_validate.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,13 @@ def validate_index(name, checks, index):
398398

399399

400400
def validate_layout_type(value):
401-
if not isinstance(value, (Component, patch_collections_abc("Callable"))):
401+
if not isinstance(
402+
value, (Component, patch_collections_abc("Callable"), list, tuple)
403+
):
402404
raise exceptions.NoLayoutException(
403405
"""
404-
Layout must be a single dash component
406+
Layout must be a single dash component, a list of dash components,
405407
or a function that returns a dash component.
406-
Cannot be a tuple (are there any trailing commas?)
407408
"""
408409
)
409410

@@ -418,18 +419,34 @@ def validate_layout(layout, layout_value):
418419
"""
419420
)
420421

421-
layout_id = stringify_id(getattr(layout_value, "id", None))
422+
component_ids = set()
422423

423-
component_ids = {layout_id} if layout_id else set()
424-
for component in layout_value._traverse(): # pylint: disable=protected-access
425-
component_id = stringify_id(getattr(component, "id", None))
426-
if component_id and component_id in component_ids:
427-
raise exceptions.DuplicateIdError(
428-
f"""
429-
Duplicate component id found in the initial layout: `{component_id}`
430-
"""
431-
)
432-
component_ids.add(component_id)
424+
def _validate(value):
425+
def _validate_id(comp):
426+
component_id = stringify_id(getattr(comp, "id", None))
427+
if component_id and component_id in component_ids:
428+
raise exceptions.DuplicateIdError(
429+
f"""
430+
Duplicate component id found in the initial layout: `{component_id}`
431+
"""
432+
)
433+
component_ids.add(component_id)
434+
435+
_validate_id(value)
436+
437+
for component in value._traverse(): # pylint: disable=protected-access
438+
_validate_id(component)
439+
440+
if isinstance(layout_value, (list, tuple)):
441+
for component in layout_value:
442+
if isinstance(component, (Component,)):
443+
_validate(component)
444+
else:
445+
raise exceptions.NoLayoutException(
446+
"List of components as layout must be a list of components only."
447+
)
448+
else:
449+
_validate(layout_value)
433450

434451

435452
def validate_template(template):

dash/dash-renderer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,6 @@
8888
],
8989
"prettier": "@plotly/prettier-config-dash",
9090
"browserslist": [
91-
"last 8 years and not dead"
91+
"last 9 years and not dead"
9292
]
9393
}

dash/dash-renderer/src/APIController.react.js

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {getAppState} from './reducers/constants';
2121
import {STATUS} from './constants/constants';
2222
import {getLoadingState, getLoadingHash} from './utils/TreeContainer';
2323
import wait from './utils/wait';
24+
import isSimpleComponent from './isSimpleComponent';
2425

2526
export const DashContext = createContext({});
2627

@@ -97,20 +98,44 @@ const UnconnectedContainer = props => {
9798

9899
content = (
99100
<DashContext.Provider value={provider.current}>
100-
<TreeContainer
101-
_dashprivate_error={error}
102-
_dashprivate_layout={layout}
103-
_dashprivate_loadingState={getLoadingState(
104-
layout,
105-
[],
106-
loadingMap
107-
)}
108-
_dashprivate_loadingStateHash={getLoadingHash(
109-
[],
110-
loadingMap
111-
)}
112-
_dashprivate_path={JSON.stringify([])}
113-
/>
101+
{Array.isArray(layout) ? (
102+
layout.map((c, i) =>
103+
isSimpleComponent(c) ? (
104+
c
105+
) : (
106+
<TreeContainer
107+
_dashprivate_error={error}
108+
_dashprivate_layout={c}
109+
_dashprivate_loadingState={getLoadingState(
110+
c,
111+
[i],
112+
loadingMap
113+
)}
114+
_dashprivate_loadingStateHash={getLoadingHash(
115+
[i],
116+
loadingMap
117+
)}
118+
_dashprivate_path={`[${i}]`}
119+
key={i}
120+
/>
121+
)
122+
)
123+
) : (
124+
<TreeContainer
125+
_dashprivate_error={error}
126+
_dashprivate_layout={layout}
127+
_dashprivate_loadingState={getLoadingState(
128+
layout,
129+
[],
130+
loadingMap
131+
)}
132+
_dashprivate_loadingStateHash={getLoadingHash(
133+
[],
134+
loadingMap
135+
)}
136+
_dashprivate_path={'[]'}
137+
/>
138+
)}
114139
</DashContext.Provider>
115140
);
116141
} else {
@@ -216,7 +241,7 @@ UnconnectedContainer.propTypes = {
216241
graphs: PropTypes.object,
217242
hooks: PropTypes.object,
218243
layoutRequest: PropTypes.object,
219-
layout: PropTypes.object,
244+
layout: PropTypes.any,
220245
loadingMap: PropTypes.any,
221246
history: PropTypes.any,
222247
error: PropTypes.object,

requires-ci.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ black==22.3.0
33
dash-flow-example==0.0.5
44
dash-dangerously-set-inner-html
55
flake8==7.0.0
6-
flaky==3.7.0
6+
flaky==3.8.1
77
flask-talisman==1.0.0
88
mimesis<=11.1.0
99
mock==4.0.3

tests/integration/clientside/test_clientside.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def update_output(value):
3535
dash_duo.wait_for_text_to_equal("#output-serverside", 'Server says "hello world"')
3636
dash_duo.wait_for_text_to_equal("#output-clientside", 'Client says "hello world"')
3737

38+
assert dash_duo.driver.execute_script("return 'dash_clientside' in window")
39+
assert dash_duo.driver.execute_script("return !('clientside' in window)")
40+
assert dash_duo.driver.execute_script("return !('ns' in window)")
41+
3842

3943
def test_clsd002_chained_serverside_clientside_callbacks(dash_duo):
4044
app = Dash(__name__, assets_folder="assets")

0 commit comments

Comments
 (0)