Skip to content

Commit 2790b1d

Browse files
authored
Merge branch 'dev' into add-more-tests
2 parents fb403dd + d098e76 commit 2790b1d

File tree

16 files changed

+282
-30
lines changed

16 files changed

+282
-30
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import MyPersistedComponentNested from './components/MyPersistedComponentNested'
77
import StyledComponent from './components/StyledComponent';
88
import WidthComponent from './components/WidthComponent';
99
import ComponentAsProp from './components/ComponentAsProp';
10+
1011
import DrawCounter from './components/DrawCounter';
12+
import AddPropsComponent from "./components/AddPropsComponent";
13+
import ReceivePropsComponent from "./components/ReceivePropsComponent";
14+
1115

1216
export {
1317
AsyncComponent,
@@ -20,4 +24,6 @@ export {
2024
WidthComponent,
2125
ComponentAsProp,
2226
DrawCounter,
27+
AddPropsComponent,
28+
ReceivePropsComponent
2329
};

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

components/dash-core-components/src/fragments/Dropdown.react.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ const Dropdown = props => {
3939
const {
4040
id,
4141
clearable,
42-
searchable,
4342
multi,
4443
options,
4544
setProps,
45+
search_value,
4646
style,
4747
loading_state,
4848
value,
@@ -122,7 +122,7 @@ const Dropdown = props => {
122122

123123
useEffect(() => {
124124
if (
125-
!searchable &&
125+
!search_value &&
126126
!isNil(sanitizedOptions) &&
127127
optionsCheck !== sanitizedOptions &&
128128
!isNil(value)

components/dash-core-components/tests/integration/dropdown/test_remove_option.py

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22

3-
from dash import Dash, html, dcc, Output, Input
3+
import pytest
4+
5+
from dash import Dash, html, dcc, Output, Input, State
46
from dash.exceptions import PreventUpdate
57

68

@@ -11,7 +13,8 @@
1113
]
1214

1315

14-
def test_ddro001_remove_option_single(dash_dcc):
16+
@pytest.mark.parametrize("searchable", (True, False))
17+
def test_ddro001_remove_option_single(dash_dcc, searchable):
1518
dropdown_options = sample_dropdown_options
1619

1720
app = Dash(__name__)
@@ -22,7 +25,7 @@ def test_ddro001_remove_option_single(dash_dcc):
2225
dcc.Dropdown(
2326
options=dropdown_options,
2427
value=value,
25-
searchable=False,
28+
searchable=searchable,
2629
id="dropdown",
2730
),
2831
html.Button("Remove option", id="remove"),
@@ -38,18 +41,17 @@ def on_click(n_clicks):
3841

3942
@app.callback(Output("value-output", "children"), [Input("dropdown", "value")])
4043
def on_change(val):
41-
if not val:
42-
raise PreventUpdate
43-
return val or "None"
44+
return val or "Nothing Here"
4445

4546
dash_dcc.start_server(app)
4647
btn = dash_dcc.wait_for_element("#remove")
4748
btn.click()
4849

49-
dash_dcc.wait_for_text_to_equal("#value-output", "None")
50+
dash_dcc.wait_for_text_to_equal("#value-output", "Nothing Here")
5051

5152

52-
def test_ddro002_remove_option_multi(dash_dcc):
53+
@pytest.mark.parametrize("searchable", (True, False))
54+
def test_ddro002_remove_option_multi(dash_dcc, searchable):
5355
dropdown_options = sample_dropdown_options
5456

5557
app = Dash(__name__)
@@ -62,7 +64,7 @@ def test_ddro002_remove_option_multi(dash_dcc):
6264
value=value,
6365
multi=True,
6466
id="dropdown",
65-
searchable=False,
67+
searchable=searchable,
6668
),
6769
html.Button("Remove option", id="remove"),
6870
html.Div(id="value-output"),
@@ -84,3 +86,64 @@ def on_change(val):
8486
btn.click()
8587

8688
dash_dcc.wait_for_text_to_equal("#value-output", '["MTL"]')
89+
90+
91+
def test_ddro003_remove_option_multiple_dropdowns(dash_dcc):
92+
app = Dash(__name__)
93+
app.layout = html.Div(
94+
[
95+
dcc.Dropdown(
96+
id="available-options",
97+
multi=True,
98+
options=sample_dropdown_options,
99+
value=["MTL", "NYC", "SF"],
100+
),
101+
dcc.Dropdown(
102+
id="chosen",
103+
multi=True,
104+
options=sample_dropdown_options,
105+
value=["NYC", "SF"],
106+
),
107+
html.Button(id="remove-btn", children="Remove"),
108+
html.Button(id="submit-btn", children="Submit"),
109+
html.Div(id="value-output"),
110+
html.Div(id="options-output"),
111+
],
112+
)
113+
114+
@app.callback(
115+
Output("chosen", "options"),
116+
Input("available-options", "value"),
117+
)
118+
def update_options(available_options):
119+
if available_options is None:
120+
return []
121+
else:
122+
return [{"label": i, "value": i} for i in available_options]
123+
124+
@app.callback(
125+
Output("available-options", "options"), [Input("remove-btn", "n_clicks")]
126+
)
127+
def on_click(n_clicks):
128+
if not n_clicks:
129+
raise PreventUpdate
130+
return sample_dropdown_options[:-1]
131+
132+
@app.callback(
133+
[Output("value-output", "children"), Output("options-output", "children")],
134+
Input("submit-btn", "n_clicks"),
135+
State("chosen", "options"),
136+
State("chosen", "value"),
137+
)
138+
def print_value(n_clicks, options, value):
139+
if not n_clicks:
140+
raise PreventUpdate
141+
return [json.dumps(value), json.dumps([i["value"] for i in options])]
142+
143+
dash_dcc.start_server(app)
144+
btn = dash_dcc.wait_for_element("#remove-btn")
145+
btn.click()
146+
btn = dash_dcc.wait_for_element("#submit-btn")
147+
btn.click()
148+
dash_dcc.wait_for_text_to_equal("#value-output", '["NYC"]')
149+
dash_dcc.wait_for_text_to_equal("#options-output", '["MTL", "NYC"]')

dash/_callback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ def register_clientside_callback(
537537
if isinstance(clientside_function, str):
538538
namespace = "_dashprivate_clientside_funcs"
539539
# Create a hash from the function, it will be the same always
540-
function_name = hashlib.md5(clientside_function.encode("utf-8")).hexdigest()
540+
function_name = hashlib.sha256(clientside_function.encode("utf-8")).hexdigest()
541541

542542
inline_scripts.append(
543543
_inline_clientside_template.format(

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/_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def _concat(x):
142142
_id = x.component_id_str().replace(".", "\\.") + "." + x.component_property
143143
if x.allow_duplicate:
144144
if not hashed_inputs:
145-
hashed_inputs = hashlib.md5(
145+
hashed_inputs = hashlib.sha256(
146146
".".join(str(x) for x in inputs).encode("utf-8")
147147
).hexdigest()
148148
# Actually adds on the property part.
@@ -213,9 +213,9 @@ def run_command_with_process(cmd):
213213
proc.communicate()
214214

215215

216-
def compute_md5(path):
216+
def compute_hash(path):
217217
with io.open(path, encoding="utf-8") as fp:
218-
return hashlib.md5(fp.read().encode("utf-8")).hexdigest()
218+
return hashlib.sha256(fp.read().encode("utf-8")).hexdigest()
219219

220220

221221
def job(msg=""):

dash/dash-renderer/src/TreeContainer.js

Lines changed: 30 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+
dissoc('children', _dashprivate_layout.props),
274+
_extraProps
275+
);
266276

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

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

486512
const children = this.getChildren(
@@ -492,7 +518,8 @@ class BaseTreeContainer extends Component {
492518
_dashprivate_layout,
493519
children,
494520
_dashprivate_loadingState,
495-
this.setProps
521+
this.setProps,
522+
_extraProps
496523
);
497524
}
498525
}

0 commit comments

Comments
 (0)