Skip to content

Commit d030e02

Browse files
authored
Merge branch 'dev' into sha256
2 parents e92e206 + 2ea44a5 commit d030e02

File tree

9 files changed

+196
-10
lines changed

9 files changed

+196
-10
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
4+
const AddPropsComponent = (props) => {
5+
const {children, id} = props;
6+
7+
8+
return (
9+
<div id={id}>
10+
{React.cloneElement(children, {
11+
receive: `Element #${id} pass`,
12+
id: id,
13+
})}
14+
</div>
15+
);
16+
};
17+
18+
AddPropsComponent.propTypes = {
19+
id: PropTypes.string,
20+
children: PropTypes.node,
21+
};
22+
23+
export default AddPropsComponent;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const ReceivePropsComponent = (props) => {
5+
const {id, text, receive} = props;
6+
7+
return (
8+
<div id={id}>
9+
{receive || text}
10+
</div>
11+
);
12+
}
13+
ReceivePropsComponent.propTypes = {
14+
id: PropTypes.string,
15+
text: PropTypes.string,
16+
receive: PropTypes.string,
17+
}
18+
19+
export default ReceivePropsComponent;

@plotly/dash-test-components/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import MyPersistedComponentNested from './components/MyPersistedComponentNested'
77
import StyledComponent from './components/StyledComponent';
88
import WidthComponent from './components/WidthComponent';
99
import ComponentAsProp from './components/ComponentAsProp';
10+
import AddPropsComponent from "./components/AddPropsComponent";
11+
import ReceivePropsComponent from "./components/ReceivePropsComponent";
1012

1113
export {
1214
AsyncComponent,
@@ -18,4 +20,6 @@ export {
1820
StyledComponent,
1921
WidthComponent,
2022
ComponentAsProp,
23+
AddPropsComponent,
24+
ReceivePropsComponent
2125
};

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
66

77
## Added
88

9+
- [#2819](https://github.com/plotly/dash/pull/2819) Add dash subcomponents receive additional parameters passed by the parent component. Fixes [#2814](https://github.com/plotly/dash/issues/2814).
10+
- [#2826](https://github.com/plotly/dash/pull/2826) When using Pages, allows for `app.title` and (new) `app.description` to be used as defaults for the page title and description. Fixes [#2811](https://github.com/plotly/dash/issues/2811).
911
- [#2795](https://github.com/plotly/dash/pull/2795) Allow list of components to be passed as layout.
10-
- [2760](https://github.com/plotly/dash/pull/2760) New additions to dcc.Loading resolving multiple issues:
12+
- [#2760](https://github.com/plotly/dash/pull/2760) New additions to dcc.Loading resolving multiple issues:
1113
- `delay_show` and `delay_hide` props to prevent flickering during brief loading periods (similar to Dash Bootstrap Components dbc.Spinner)
1214
- `overlay_style` for styling the loading overlay, such as setting visibility and opacity for children
1315
- `target_components` specifies components/props triggering the loading spinner

dash/_pages.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,14 @@ def register_page(
235235
order `0`
236236
237237
- `title`:
238-
(string or function) The name of the page <title>. That is, what appears in the browser title.
239-
If not supplied, will use the supplied `name` or will be inferred by module,
240-
e.g. `pages.weekly_analytics` to `Weekly analytics`
238+
(string or function) Specifies the page title displayed in the browser tab.
239+
If not supplied, the app's title is used if different from the default "Dash".
240+
Otherwise, the title is the given `name` or inferred from the module name.
241+
For example, `pages.weekly_analytics` is inferred as "Weekly Analytics".
241242
242243
- `description`:
243244
(string or function) The <meta type="description"></meta>.
244-
If not supplied, then nothing is supplied.
245+
If not defined, the application description will be used if available.
245246
246247
- `image`:
247248
The meta description image used by social media platforms.
@@ -319,10 +320,18 @@ def register_page(
319320
)
320321
page.update(
321322
supplied_title=title,
322-
title=(title if title is not None else page["name"]),
323+
title=title
324+
if title is not None
325+
else CONFIG.title
326+
if CONFIG.title != "Dash"
327+
else page["name"],
323328
)
324329
page.update(
325-
description=description if description else "",
330+
description=description
331+
if description
332+
else CONFIG.description
333+
if CONFIG.description
334+
else "",
326335
order=order,
327336
supplied_order=order,
328337
supplied_layout=layout,

dash/dash-renderer/src/TreeContainer.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
map,
1717
mapObjIndexed,
1818
mergeRight,
19+
omit,
1920
pick,
2021
pickBy,
2122
propOr,
@@ -237,7 +238,13 @@ class BaseTreeContainer extends Component {
237238
);
238239
}
239240

240-
getComponent(_dashprivate_layout, children, loading_state, setProps) {
241+
getComponent(
242+
_dashprivate_layout,
243+
children,
244+
loading_state,
245+
setProps,
246+
_extraProps
247+
) {
241248
const {_dashprivate_config, _dashprivate_dispatch, _dashprivate_error} =
242249
this.props;
243250

@@ -262,7 +269,10 @@ class BaseTreeContainer extends Component {
262269
],
263270
_dashprivate_config
264271
);
265-
let props = dissoc('children', _dashprivate_layout.props);
272+
let props = mergeRight(
273+
_extraProps,
274+
dissoc('children', _dashprivate_layout.props)
275+
);
266276

267277
for (let i = 0; i < childrenProps.length; i++) {
268278
const childrenProp = childrenProps[i];
@@ -481,6 +491,21 @@ class BaseTreeContainer extends Component {
481491
_dashprivate_path
482492
} = this.props;
483493

494+
const _extraProps = omit(
495+
[
496+
'_dashprivate_error',
497+
'_dashprivate_layout',
498+
'_dashprivate_loadingState',
499+
'_dashprivate_loadingStateHash',
500+
'_dashprivate_path',
501+
'_dashprivate_config',
502+
'_dashprivate_dispatch',
503+
'_dashprivate_graphs',
504+
'_dashprivate_loadingMap'
505+
],
506+
this.props
507+
);
508+
484509
const layoutProps = this.getLayoutProps();
485510

486511
const children = this.getChildren(
@@ -492,7 +517,8 @@ class BaseTreeContainer extends Component {
492517
_dashprivate_layout,
493518
children,
494519
_dashprivate_loadingState,
495-
this.setProps
520+
this.setProps,
521+
_extraProps
496522
);
497523
}
498524
}

dash/dash.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ class Dash:
366366
functions. The syntax for this parameter is a dict of State objects:
367367
`routing_callback_inputs={"language": Input("language", "value")}`
368368
NOTE: the keys "pathname_" and "search_" are reserved for internal use.
369+
370+
:param description: Sets a default description for meta tags on Dash pages (use_pages=True).
371+
369372
"""
370373

371374
_plotlyjs_url: str
@@ -404,6 +407,7 @@ def __init__( # pylint: disable=too-many-statements
404407
add_log_handler=True,
405408
hooks: Union[RendererHooks, None] = None,
406409
routing_callback_inputs: Optional[Dict[str, Union[Input, State]]] = None,
410+
description=None,
407411
**obsolete,
408412
):
409413
_validate.check_obsolete(obsolete)
@@ -458,6 +462,7 @@ def __init__( # pylint: disable=too-many-statements
458462
title=title,
459463
update_title=update_title,
460464
include_pages_meta=include_pages_meta,
465+
description=description,
461466
)
462467
self.config.set_read_only(
463468
[

tests/integration/multi_page/test_pages_layout.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,27 @@ def test_pala005_routing_inputs(dash_duo, clear_pages_state):
235235
# Changing the language Input re-runs the layout function
236236
dash_duo.select_dcc_dropdown("#language", "fr")
237237
dash_duo.wait_for_text_to_equal("#contents", "Le hash dit: #123")
238+
239+
240+
def get_app_title_description():
241+
app = Dash(
242+
__name__, use_pages=True, title="App Title", description="App Description"
243+
)
244+
dash.register_page("home", layout=html.Div("Home"), path="/")
245+
dash.register_page(
246+
"page1",
247+
layout=html.Div("Page1"),
248+
title="Page 1 Title",
249+
description="Page 1 Description",
250+
)
251+
app.layout = html.Div(dash.page_container)
252+
return app
253+
254+
255+
def test_pala006_app_title_discription(dash_duo, clear_pages_state):
256+
dash_duo.start_server(get_app_title_description())
257+
258+
assert dash.page_registry["home"]["title"] == "App Title"
259+
assert dash.page_registry["page1"]["title"] == "Page 1 Title"
260+
assert dash.page_registry["home"]["description"] == "App Description"
261+
assert dash.page_registry["page1"]["description"] == "Page 1 Description"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from dash import Dash, html, Input, Output, no_update
2+
3+
from dash_test_components import AddPropsComponent, ReceivePropsComponent
4+
5+
6+
def test_rdarp001_add_receive_props(dash_duo):
7+
app = Dash(__name__)
8+
app.layout = html.Div(
9+
[
10+
AddPropsComponent(
11+
ReceivePropsComponent(
12+
id="test-receive1",
13+
text="receive component1",
14+
),
15+
id="test-add",
16+
),
17+
ReceivePropsComponent(
18+
id="test-receive2",
19+
text="receive component2",
20+
),
21+
html.Button(
22+
"load no pass props",
23+
id="load-no-pass-props",
24+
),
25+
html.Pre("load-no-pass-props-output"),
26+
html.Div(id="load-no-pass-props-output"),
27+
html.Button(
28+
"load pass props",
29+
id="load-pass-props",
30+
),
31+
html.Pre("load-pass-props-output"),
32+
html.Div(id="load-pass-props-output"),
33+
]
34+
)
35+
36+
@app.callback(
37+
Output("load-no-pass-props-output", "children"),
38+
Input("load-no-pass-props", "n_clicks"),
39+
)
40+
def load_no_pass_props(n_clicks):
41+
if n_clicks:
42+
return ReceivePropsComponent(
43+
id="test-receive-no-pass",
44+
text="receive component no pass",
45+
)
46+
return no_update
47+
48+
@app.callback(
49+
Output("load-pass-props-output", "children"),
50+
Input("load-pass-props", "n_clicks"),
51+
)
52+
def load_pass_props(n_clicks):
53+
if n_clicks:
54+
return AddPropsComponent(
55+
ReceivePropsComponent(
56+
id="test-receive-pass",
57+
text="receive component pass",
58+
),
59+
id="test-add-pass",
60+
)
61+
return no_update
62+
63+
dash_duo.start_server(app)
64+
dash_duo.wait_for_text_to_equal("#test-receive1", "Element #test-add pass")
65+
dash_duo.wait_for_text_to_equal("#test-receive2", "receive component2")
66+
67+
clicker_no_pass = dash_duo.wait_for_element("#load-no-pass-props")
68+
clicker_no_pass.click()
69+
dash_duo.wait_for_text_to_equal(
70+
"#test-receive-no-pass", "receive component no pass"
71+
)
72+
clicker_pass = dash_duo.wait_for_element("#load-pass-props")
73+
clicker_pass.click()
74+
dash_duo.wait_for_text_to_equal("#test-receive-pass", "Element #test-add-pass pass")

0 commit comments

Comments
 (0)