Skip to content

Commit b4e33ab

Browse files
committed
Merge branch 'v4' into feature/dcc-refactor-datepicker
2 parents ecf7331 + 8bbd5f9 commit b4e33ab

File tree

26 files changed

+487
-66
lines changed

26 files changed

+487
-66
lines changed

@plotly/dash-generator-test-component-typescript/src/props.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// Needs to export types if not in a d.ts file or if any import is present in the d.ts
22
import React from 'react';
3-
import {DashComponent} from '@dash-renderer/types';
3+
4+
type DashComponent = {
5+
props: string;
6+
namespace: string;
7+
children?: [];
8+
}
49

510

611
type Nested = {

@plotly/dash-generator-test-component-typescript/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"jsx": "react",
44
"baseUrl": "src/ts",
55
"paths": {
6-
"@dash-renderer/*": ["../../../../dash/dash-renderer/src/*"]
76
},
87
"inlineSources": true,
98
"sourceMap": true,

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,15 @@ This project adheres to [Semantic Versioning](https://semver.org/).
4545
- [#3395](https://github.com/plotly/dash/pull/3396) Add position argument to hooks.devtool
4646
- [#3403](https://github.com/plotly/dash/pull/3403) Add app_context to get_app, allowing to get the current app in routes.
4747
- [#3407](https://github.com/plotly/dash/pull/3407) Add `hidden` to callback arguments, hiding the callback from appearing in the devtool callback graph.
48+
- [#3397](https://github.com/plotly/dash/pull/3397) Add optional callbacks, suppressing callback warning for missing component ids for a single callback.
4849
- [#3424](https://github.com/plotly/dash/pull/3424) Adds support for `Patch` on clientside callbacks class `dash_clientside.Patch`, as well as supporting side updates, eg: (Running, SetProps).
4950
- [#3347](https://github.com/plotly/dash/pull/3347) Added 'api_endpoint' to `callback` to expose api endpoints at the provided path for use to be executed directly without dash.
51+
- [#3445](https://github.com/plotly/dash/pull/3445) Added API to reverse direction of slider component.
52+
- [#3460](https://github.com/plotly/dash/pull/3460) Add `/health` endpoint for server monitoring and health checks.
53+
- [#3465](https://github.com/plotly/dash/pull/3465) Plotly cloud integrations, add devtool API, placeholder plotly cloud CLI & publish button, `dash[cloud]` extra dependencies.
5054

5155
## Fixed
5256
- [#3395](https://github.com/plotly/dash/pull/3395) Fix Components added through set_props() cannot trigger related callback functions. Fix [#3316](https://github.com/plotly/dash/issues/3316)
53-
- [#3397](https://github.com/plotly/dash/pull/3397) Add optional callbacks, suppressing callback warning for missing component ids for a single callback.
5457
- [#3415](https://github.com/plotly/dash/pull/3415) Fix the error triggered when only a single no_update is returned for client-side callback functions with multiple Outputs. Fix [#3366](https://github.com/plotly/dash/issues/3366)
5558
- [#3416](https://github.com/plotly/dash/issues/3416) Fix DeprecationWarning in dash/_jupyter.py by migrating from deprecated ipykernel.comm.Comm to comm module
5659

components/dash-core-components/src/components/Tabs.tsx

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useCallback, useEffect, useRef, useState} from 'react';
1+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import {has, isNil} from 'ramda';
33

44
import LoadingElement from '../utils/_LoadingElement';
@@ -34,20 +34,33 @@ const EnhancedTab = ({
3434
// We use the raw path here since it's up one level from
3535
// the tabs child.
3636
const isLoading = ctx.useLoading({rawPath: componentPath});
37-
const tabStyle = {
38-
...style,
39-
...(disabled ? disabled_style : {}),
40-
...(selected ? selected_style : {}),
41-
};
37+
const tabStyle = useMemo(() => {
38+
return {
39+
...style,
40+
...(disabled ? disabled_style : {}),
41+
...(selected ? selected_style : {}),
42+
};
43+
}, [style, disabled, disabled_style, selected, selected_style]);
4244

43-
const tabClassNames = [
44-
'tab',
45-
className,
46-
disabled ? 'tab--disabled' : null,
47-
disabled ? disabled_className : null,
48-
selected ? 'tab--selected' : null,
49-
selected ? selected_className : null,
50-
].filter(Boolean);
45+
const tabClassNames = useMemo(() => {
46+
let names = 'tab';
47+
if (disabled) {
48+
names += ' tab--disabled';
49+
if (disabled_className) {
50+
names += ` ${disabled_className}`;
51+
}
52+
}
53+
if (selected) {
54+
names += ' tab--selected';
55+
if (selected_className) {
56+
names += ` ${selected_className}`;
57+
}
58+
}
59+
if (className) {
60+
names += ` ${className}`;
61+
}
62+
return names;
63+
}, [className, disabled, disabled_className, selected, selected_className]);
5164

5265
let labelDisplay;
5366
if (typeof label === 'object') {
@@ -64,7 +77,7 @@ const EnhancedTab = ({
6477
return (
6578
<div
6679
data-dash-is-loading={isLoading}
67-
className={tabClassNames.join(' ')}
80+
className={tabClassNames}
6881
id={id}
6982
style={tabStyle}
7083
onClick={() => {
@@ -121,9 +134,9 @@ function Tabs({
121134
const firstChildren: TabProps = window.dash_component_api.getLayout(
122135
[...children[0].props.componentPath, 'props']
123136
);
124-
return firstChildren.value;
137+
return firstChildren.value ?? 'tab-1';
125138
}
126-
return undefined;
139+
return 'tab-1';
127140
};
128141

129142
// Initialize value on mount if not set
@@ -217,24 +230,41 @@ function Tabs({
217230

218231
const selectedTabContent = !isNil(selectedTab) ? selectedTab : '';
219232

220-
const tabContainerClassNames = [
221-
'tab-container',
222-
vertical ? 'tab-container--vert' : null,
223-
props.className,
224-
].filter(Boolean);
225-
226-
const tabContentClassNames = [
227-
'tab-content',
228-
vertical ? 'tab-content--vert' : null,
229-
props.content_className,
230-
].filter(Boolean);
231-
232-
const tabParentClassNames = [
233-
'tab-parent',
234-
vertical ? ' tab-parent--vert' : null,
235-
isAboveBreakpoint ? ' tab-parent--above-breakpoint' : null,
236-
props.parent_className,
237-
].filter(Boolean);
233+
const tabContainerClassNames = useMemo(() => {
234+
let names = 'tab-container';
235+
if (vertical) {
236+
names += ` tab-container--vert`;
237+
}
238+
if (props.className) {
239+
names += ` ${props.className}`;
240+
}
241+
return names;
242+
}, [vertical, props.className]);
243+
244+
const tabContentClassNames = useMemo(() => {
245+
let names = 'tab-content';
246+
if (vertical) {
247+
names += ` tab-content--vert`;
248+
}
249+
if (props.content_className) {
250+
names += ` ${props.content_className}`;
251+
}
252+
return names;
253+
}, [vertical, props.content_className]);
254+
255+
const tabParentClassNames = useMemo(() => {
256+
let names = 'tab-parent';
257+
if (vertical) {
258+
names += ` tab-parent--vert`;
259+
}
260+
if (isAboveBreakpoint) {
261+
names += ' tab-parent--above-breakpoint';
262+
}
263+
if (props.parent_className) {
264+
names += ` ${props.parent_className}`;
265+
}
266+
return names;
267+
}, [vertical, isAboveBreakpoint, props.parent_className]);
238268

239269
// Set CSS variables for dynamic styling
240270
const cssVars = {
@@ -247,20 +277,20 @@ function Tabs({
247277
<LoadingElement>
248278
{loadingProps => (
249279
<div
250-
className={tabParentClassNames.join(' ')}
280+
className={tabParentClassNames}
251281
style={{...cssVars, ...props.parent_style}}
252282
id={props.id ? `${props.id}-parent` : undefined}
253283
{...loadingProps}
254284
>
255285
<div
256-
className={tabContainerClassNames.join(' ')}
286+
className={tabContainerClassNames}
257287
style={props.style}
258288
id={props.id}
259289
>
260290
{EnhancedTabs}
261291
</div>
262292
<div
263-
className={tabContentClassNames.join(' ')}
293+
className={tabContentClassNames}
264294
style={props.content_style}
265295
>
266296
{selectedTabContent || ''}
@@ -271,7 +301,11 @@ function Tabs({
271301
);
272302
}
273303

274-
Tabs.dashPersistence = true;
304+
Tabs.dashPersistence = {
305+
persisted_props: [PersistedProps.value],
306+
persistence_type: PersistenceTypes.local,
307+
};
308+
275309
Tabs.dashChildrenUpdate = true;
276310

277311
export default Tabs;

components/dash-core-components/src/components/css/tabs.css

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@
2626
transition: background-color, color 200ms;
2727
text-align: center;
2828
box-sizing: border-box;
29-
}
30-
31-
.tab:hover {
3229
cursor: pointer;
3330
}
3431

@@ -45,7 +42,8 @@
4542

4643
/* Tab disabled state */
4744
.tab--disabled {
48-
color: #d6d6d6;
45+
opacity: 0.6;
46+
cursor: not-allowed;
4947
}
5048

5149
/* Tab content area */

components/dash-core-components/tests/integration/misc/test_platter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
def test_mspl001_dcc_components_platter(platter_app, dash_dcc):
9+
dash_dcc.driver.set_window_size(800, 600)
910
dash_dcc.start_server(platter_app)
1011

1112
dash_dcc.wait_for_element("#waitfor")

components/dash-core-components/tests/integration/tab/test_tabs.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def test_tabs001_in_vertical_mode(dash_dcc):
3434
]
3535
)
3636

37+
dash_dcc.driver.set_window_size(800, 600)
3738
dash_dcc.start_server(app)
3839
dash_dcc.wait_for_text_to_equal("#tab-3", "Tab three")
3940
dash_dcc.percy_snapshot("Core Tabs - vertical mode")
@@ -67,6 +68,7 @@ def render_content(tab):
6768
elif tab == "tab-2":
6869
return html.Div([html.H3("Test content 2")], id="test-tab-2")
6970

71+
dash_dcc.driver.set_window_size(800, 600)
7072
dash_dcc.start_server(app)
7173
dash_dcc.wait_for_text_to_equal("#tabs-content", "Test content 2")
7274
dash_dcc.percy_snapshot("Core initial tab - tab 2")
@@ -87,6 +89,7 @@ def test_tabs003_without_children_undefined(dash_dcc):
8789
id="app",
8890
)
8991

92+
dash_dcc.driver.set_window_size(800, 600)
9093
dash_dcc.start_server(app)
9194
dash_dcc.wait_for_element("#tabs-content")
9295
assert dash_dcc.find_element("#app").text == "Dash Tabs component demo"
@@ -122,6 +125,7 @@ def render_content(tab):
122125
elif tab == "tab-2":
123126
return html.H3("Tab content 2")
124127

128+
dash_dcc.driver.set_window_size(800, 600)
125129
dash_dcc.start_server(app)
126130
dash_dcc.wait_for_text_to_equal("#tabs-content", "Default selected Tab content 1")
127131
assert dash_dcc.get_logs() == []
@@ -155,6 +159,7 @@ def test_tabs005_disabled(dash_dcc):
155159
]
156160
)
157161

162+
dash_dcc.driver.set_window_size(800, 600)
158163
dash_dcc.start_server(app)
159164

160165
dash_dcc.wait_for_element("#tab-2")

components/dash-core-components/tests/integration/tab/test_tabs_with_graphs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def render_content(tab):
6464
]
6565
)
6666

67+
dash_dcc.driver.set_window_size(800, 600)
6768
dash_dcc.start_server(app)
6869

6970
tab_one = dash_dcc.wait_for_element("#tab-1")
@@ -156,6 +157,7 @@ def on_click_update_graph(n_clicks):
156157
"layout": {"width": 700, "height": 450},
157158
}
158159

160+
dash_dcc.driver.set_window_size(800, 600)
159161
dash_dcc.start_server(app)
160162

161163
button_one = dash_dcc.wait_for_element("#one")

dash/_plotly_cli.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import sys
2+
3+
4+
def cli():
5+
try:
6+
from plotly_cloud.cli import main # pylint: disable=import-outside-toplevel
7+
8+
main()
9+
except ImportError:
10+
print(
11+
"Plotly cloud is not installed,"
12+
" install it with `pip install dash[cloud]` to use the plotly command",
13+
file=sys.stderr,
14+
)
15+
sys.exit(-1)

dash/background_callback/managers/diskcache_manager.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,14 @@ async def async_run():
287287
}
288288
},
289289
)
290+
290291
if asyncio.iscoroutine(user_callback_output):
291292
user_callback_output = await user_callback_output
292293
if not errored:
293-
cache.set(result_key, user_callback_output)
294+
try:
295+
cache.set(result_key, user_callback_output)
296+
except Exception as err: # pylint: disable=broad-except
297+
print(f"Diskcache manager couldn't save output: {err}")
294298

295299
if asyncio.iscoroutinefunction(fn):
296300
func = partial(ctx.run, async_run)

0 commit comments

Comments
 (0)