Skip to content

Commit 791c1eb

Browse files
authored
Merge pull request #2884 from plotly/master-2.17.1
Master 2.17.1
2 parents 95520f7 + 18f2f85 commit 791c1eb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+894
-261
lines changed

.circleci/config.yml

+21-21
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ jobs:
1919
steps:
2020
- checkout
2121
- run: echo $PYVERSION > ver.txt
22-
- run: cat requires-*.txt > requires-all.txt
22+
- run: cat requirements/*.txt > requirements-all.txt
2323
- restore_cache:
24-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
24+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
2525
- attach_workspace:
2626
at: ~/dash
2727
- store_artifacts:
@@ -42,9 +42,9 @@ jobs:
4242
steps:
4343
- checkout
4444
- run: echo $PYVERSION > ver.txt
45-
- run: cat requires-*.txt > requires-all.txt
45+
- run: cat requirements/*.txt > requirements-all.txt
4646
- restore_cache:
47-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
47+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
4848
- run:
4949
name: 🏁 Build Component Packages & Update Dependencies/Artifacts
5050
command: |
@@ -67,7 +67,7 @@ jobs:
6767
cat /home/circleci/.npm/_logs/*
6868
fi
6969
- save_cache:
70-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
70+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
7171
paths:
7272
- venv
7373
- persist_to_workspace:
@@ -101,9 +101,9 @@ jobs:
101101
- checkout
102102
- run: sudo apt-get update
103103
- run: echo $PYVERSION > ver.txt
104-
- run: cat requires-*.txt > requires-all.txt
104+
- run: cat requirements/*.txt > requirements-all.txt
105105
- restore_cache:
106-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
106+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
107107
- browser-tools/install-browser-tools:
108108
chrome-version: 120.0.6099.71
109109
- run:
@@ -191,9 +191,9 @@ jobs:
191191
path: ~/dash
192192
- run: sudo apt-get update
193193
- run: echo $PYVERSION > ver.txt
194-
- run: cat requires-*.txt > requires-all.txt
194+
- run: cat requirements/*.txt > requirements-all.txt
195195
- restore_cache:
196-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
196+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
197197
- browser-tools/install-browser-tools:
198198
chrome-version: 120.0.6099.71
199199
install-firefox: false
@@ -268,9 +268,9 @@ jobs:
268268
- checkout:
269269
path: ~/dash
270270
- run: echo $PYVERSION > ver.txt
271-
- run: cat requires-*.txt > requires-all.txt
271+
- run: cat requirements/*.txt > requirements-all.txt
272272
- restore_cache:
273-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
273+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
274274
- run:
275275
name: 🌸 Lint
276276
command: |
@@ -308,9 +308,9 @@ jobs:
308308
path: ~/dash
309309
- run: sudo apt-get update
310310
- run: echo $PYVERSION > ver.txt
311-
- run: cat requires-*.txt > requires-all.txt
311+
- run: cat requirements/*.txt > requirements-all.txt
312312
- restore_cache:
313-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
313+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
314314
- browser-tools/install-browser-tools:
315315
chrome-version: 120.0.6099.71
316316
install-firefox: false
@@ -377,9 +377,9 @@ jobs:
377377
path: ~/dash
378378
- run: sudo apt-get update
379379
- run: echo $PYVERSION > ver.txt
380-
- run: cat requires-*.txt > requires-all.txt
380+
- run: cat requirements/*.txt > requirements-all.txt
381381
- restore_cache:
382-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
382+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
383383
- restore_cache:
384384
key: html-{{ checksum "components/dash-html-components/package.json" }}-{{ checksum "components/dash-html-components/package-lock.json" }}
385385
- browser-tools/install-browser-tools:
@@ -453,9 +453,9 @@ jobs:
453453
path: ~/dash
454454
- run: sudo apt-get update
455455
- run: echo $PYVERSION > ver.txt
456-
- run: cat requires-*.txt > requires-all.txt
456+
- run: cat requirements/*.txt > requirements-all.txt
457457
- restore_cache:
458-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
458+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
459459
- browser-tools/install-browser-tools:
460460
chrome-version: 120.0.6099.71
461461
install-firefox: false
@@ -507,9 +507,9 @@ jobs:
507507
path: ~/dash
508508
- run: sudo apt-get update
509509
- run: echo $PYVERSION > ver.txt
510-
- run: cat requires-*.txt > requires-all.txt
510+
- run: cat requirements/*.txt > requirements-all.txt
511511
- restore_cache:
512-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
512+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
513513
- restore_cache:
514514
key: table-{{ checksum "components/dash-table/package.json" }}-{{ checksum "components/dash-table/package-lock.json" }}
515515
- browser-tools/install-browser-tools:
@@ -581,9 +581,9 @@ jobs:
581581
- checkout:
582582
path: ~/dash
583583
- run: echo $PYVERSION > ver.txt
584-
- run: cat requires-*.txt > requires-all.txt
584+
- run: cat requirements/*.txt > requirements-all.txt
585585
- restore_cache:
586-
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requires-all.txt" }}
586+
key: dep-{{ checksum ".circleci/config.yml" }}-{{ checksum "ver.txt" }}-{{ checksum "requirements-all.txt" }}
587587
- restore_cache:
588588
key: dep-{{ .Branch }}-{{ checksum "components/dash-table/package-lock.json" }}-{{ checksum "components/dash-table/package.json" }}
589589
- run:

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

5+
## [2.17.1] - 2024-06-12
6+
7+
## Fixed
8+
9+
- [#2860](https://github.com/plotly/dash/pull/2860) Fix dcc.Loading to apply overlay_style only to the children and not the spinner. Fixes [#2858](https://github.com/plotly/dash/issues/2858)
10+
- [#2854](https://github.com/plotly/dash/pull/2854) Fix dcc.Dropdown resetting empty values to null and triggering callbacks. Fixes [#2850](https://github.com/plotly/dash/issues/2850)
11+
- [#2859](https://github.com/plotly/dash/pull/2859) Fix base patch operators. fixes [#2855](https://github.com/plotly/dash/issues/2855)
12+
- [#2856](https://github.com/plotly/dash/pull/2856) Fix multiple consecutive calls with same id to set_props only keeping the last props. Fixes [#2852](https://github.com/plotly/dash/issues/2852)
13+
- [#2867](https://github.com/plotly/dash/pull/2867) Fix clientside no output callback. Fixes [#2866](https://github.com/plotly/dash/issues/2866)
14+
- [#2876](https://github.com/plotly/dash/pull/2876) Fix pattern matching in callback running argument. Fixes [#2863](https://github.com/plotly/dash/issues/2863)
15+
516
## [2.17.0] - 2024-05-03
617

718
## Added

MANIFEST.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include README.md
22
include LICENSE
3-
include requires-*.txt
3+
include requirements/*.txt
44
include dash/favicon.ico
55
include dash/extract-meta.js
66
include dash/deps/*.js

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ Built on top of [Plotly.js](https://github.com/plotly/plotly.js), [React](https:
1414

1515
- [dash.gallery](https://dash.gallery): Dash app gallery with Python & R code
1616

17+
<div align="center">
18+
<a href="https://dash.plotly.com/project-maintenance">
19+
<img src="https://dash.plotly.com/assets/images/maintained-by-plotly.png" width="400px" alt="Maintained by Plotly">
20+
</a>
21+
</div>
22+
23+
1724
### Dash App Examples
1825

1926
| Dash App | Description |

components/dash-core-components/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-core-components/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-core-components",
3-
"version": "2.14.0",
3+
"version": "2.14.1",
44
"description": "Core component suite for Dash",
55
"repository": {
66
"type": "git",

components/dash-core-components/src/components/Loading.react.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import DefaultSpinner from '../fragments/Loading/spinners/DefaultSpinner.jsx';
55
import CubeSpinner from '../fragments/Loading/spinners/CubeSpinner.jsx';
66
import CircleSpinner from '../fragments/Loading/spinners/CircleSpinner.jsx';
77
import DotSpinner from '../fragments/Loading/spinners/DotSpinner.jsx';
8-
import {mergeRight} from 'ramda';
98

109
const spinnerComponentOptions = {
1110
graph: GraphSpinner,
@@ -49,10 +48,6 @@ const Loading = ({
4948
justifyContent: 'center',
5049
alignItems: 'center',
5150
};
52-
const hiddenContainer = mergeRight(
53-
{visibility: 'hidden', position: 'relative'},
54-
overlay_style
55-
);
5651

5752
/* Overrides default Loading behavior if target_components is set. By default,
5853
* Loading fires when any recursive child enters loading state. This makes loading
@@ -132,14 +127,23 @@ const Loading = ({
132127

133128
return (
134129
<div
130+
style={{position: 'relative', ...parent_style}}
135131
className={parent_className}
136-
style={
137-
showSpinner
138-
? mergeRight(hiddenContainer, parent_style)
139-
: parent_style
140-
}
141132
>
142-
{children}
133+
<div
134+
className={parent_className}
135+
style={
136+
showSpinner
137+
? {
138+
visibility: 'hidden',
139+
...overlay_style,
140+
...parent_style,
141+
}
142+
: parent_style
143+
}
144+
>
145+
{children}
146+
</div>
143147
<div style={showSpinner ? coveringSpinner : {}}>
144148
{showSpinner &&
145149
(custom_spinner || (

components/dash-core-components/src/components/Tabs.react.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export default class Tabs extends Component {
159159
parseChildrenToArray() {
160160
if (this.props.children && !is(Array, this.props.children)) {
161161
// if dcc.Tabs.children contains just one single element, it gets passed as an object
162-
// instead of an array - so we put in in a array ourselves!
162+
// instead of an array - so we put it in an array ourselves!
163163
return [this.props.children];
164164
}
165165
return this.props.children;

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {isNil, pluck, without, pick} from 'ramda';
1+
import {isNil, pluck, without, pick, isEmpty} from 'ramda';
22
import React, {useState, useCallback, useEffect, useMemo, useRef} from 'react';
33
import ReactDropdown from 'react-virtualized-select';
44
import createFilterOptions from 'react-select-fast-filter-options';
@@ -125,7 +125,8 @@ const Dropdown = props => {
125125
!search_value &&
126126
!isNil(sanitizedOptions) &&
127127
optionsCheck !== sanitizedOptions &&
128-
!isNil(value)
128+
!isNil(value) &&
129+
!isEmpty(value)
129130
) {
130131
const values = sanitizedOptions.map(option => option.value);
131132
if (multi && Array.isArray(value)) {

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

+43-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import pytest
44

5-
from dash import Dash, html, dcc, Output, Input, State
5+
from dash import Dash, html, dcc, Output, Input, State, Patch
66
from dash.exceptions import PreventUpdate
77

8+
from selenium.webdriver.common.keys import Keys
89

910
sample_dropdown_options = [
1011
{"label": "New York City", "value": "NYC"},
@@ -147,3 +148,44 @@ def print_value(n_clicks, options, value):
147148
btn.click()
148149
dash_dcc.wait_for_text_to_equal("#value-output", '["NYC"]')
149150
dash_dcc.wait_for_text_to_equal("#options-output", '["MTL", "NYC"]')
151+
152+
153+
def test_ddro004_empty_string_not_updated(dash_dcc):
154+
# The value should stay as empty string and not null.
155+
app = Dash()
156+
app.layout = html.Div(
157+
[
158+
dcc.Dropdown(["a", "b", "c"], value="", id="drop"),
159+
html.Div(id="output"),
160+
dcc.Store(data={"count": 0}, id="count"),
161+
html.Div(id="count-output"),
162+
]
163+
)
164+
165+
@app.callback(
166+
Output("output", "children"),
167+
Output("count", "data"),
168+
Input("drop", "value"),
169+
)
170+
def on_value(value):
171+
count = Patch()
172+
count.count += 1
173+
if value is None:
174+
return "Value is none", count
175+
return f"Value={value}", count
176+
177+
app.clientside_callback(
178+
"data => data.count", Output("count-output", "children"), Input("count", "data")
179+
)
180+
181+
dash_dcc.start_server(app)
182+
dash_dcc.wait_for_text_to_equal("#output", "Value=")
183+
184+
dash_dcc.wait_for_text_to_equal("#count-output", "1")
185+
186+
select_input = dash_dcc.find_element("#drop input")
187+
select_input.send_keys("a")
188+
select_input.send_keys(Keys.ENTER)
189+
190+
dash_dcc.wait_for_text_to_equal("#output", "Value=a")
191+
dash_dcc.wait_for_text_to_equal("#count-output", "2")

components/dash-core-components/tests/integration/loading/test_loading_component.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ def updateDiv(n_clicks):
406406
dash_dcc.find_element(".loading .dash-spinner")
407407
# unlike the default, the content should be visible
408408
dash_dcc.wait_for_text_to_equal("#div-1", "content")
409-
dash_dcc.wait_for_style_to_equal("#root > div", "opacity", "0.5")
409+
dash_dcc.wait_for_style_to_equal("#root > div > div", "opacity", "0.5")
410410

411411
dash_dcc.wait_for_text_to_equal("#div-1", "changed")
412412

components/dash-table/src/dash-table/dash/DataTable.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ export const propTypes = {
700700
/**
701701
* Denotes the format of the headers in the export data file.
702702
* If `'none'`, there will be no header. If `'display'`, then the header
703-
* of the data file will be be how it is currently displayed. Note that
703+
* of the data file will be how it is currently displayed. Note that
704704
* `'display'` is only supported for `'xlsx'` export_format and will behave
705705
* like `'names'` for `'csv'` export format. If `'ids'` or `'names'`,
706706
* then the headers of data file will be the column id or the column
@@ -802,7 +802,7 @@ export const propTypes = {
802802
* If `'native'`, then the sorting UI is displayed and the sorting
803803
* logic is handled by the table. That is, it is performed on the data
804804
* that exists in the `data` property.
805-
* If `'custom'`, the the sorting UI is displayed but it is the
805+
* If `'custom'`, the sorting UI is displayed but it is the
806806
* responsibility of the developer to program the sorting
807807
* through a callback (where `sort_by` would be the input and `data`
808808
* would be the output).

dash/_callback.py

+2
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ def register_clientside_callback(
549549
**kwargs,
550550
):
551551
output, inputs, state, prevent_initial_call = handle_callback_args(args, kwargs)
552+
no_output = isinstance(output, (list,)) and len(output) == 0
552553
insert_callback(
553554
callback_list,
554555
callback_map,
@@ -559,6 +560,7 @@ def register_clientside_callback(
559560
state,
560561
None,
561562
prevent_initial_call,
563+
no_output=no_output,
562564
)
563565

564566
# If JS source is explicitly given, create a namespace and function

dash/_callback_context.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,11 @@ def timing_information(self):
252252
def set_props(self, component_id: typing.Union[str, dict], props: dict):
253253
ctx_value = _get_context_value()
254254
_id = stringify_id(component_id)
255-
ctx_value.updated_props[_id] = props
255+
existing = ctx_value.updated_props.get(_id)
256+
if existing is not None:
257+
ctx_value.updated_props[_id] = {**existing, **props}
258+
else:
259+
ctx_value.updated_props[_id] = props
256260

257261

258262
callback_context = CallbackContext()

dash/_dash_renderer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22

3-
__version__ = "1.20.0"
3+
__version__ = "1.20.1"
44

55
_available_react_versions = {"16.14.0", "18.2.0"}
66
_available_reactdom_versions = {"16.14.0", "18.2.0"}
@@ -64,7 +64,7 @@ def _set_react_version(v_react, v_reactdom=None):
6464
{
6565
"relative_package_path": "dash-renderer/build/dash_renderer.min.js",
6666
"dev_package_path": "dash-renderer/build/dash_renderer.dev.js",
67-
"external_url": "https://unpkg.com/dash-renderer@1.20.0"
67+
"external_url": "https://unpkg.com/dash-renderer@1.20.1"
6868
"/build/dash_renderer.min.js",
6969
"namespace": "dash",
7070
},

0 commit comments

Comments
 (0)