Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#3646](https://github.com/plotly/dash/pull/3646) Remove React 16 support (`16.14.0` is no longer an accepted value for `REACT_VERSION` / `_set_react_version`).

### Changed
- [#4015](https://github.com/plotly/dash/pull/4015) Replace the `circle` spinner in `dcc.Loading` with a rotating ring, ported from react-spinners. Apps using `type="circle"` will see the new spinner.
- [#4026](https://github.com/plotly/dash/pull/4026) Log a warning, once per process, when a streaming request is refused because its stream token failed verification, which on multi-worker deployments usually means the workers do not share a signing secret.
- [#3987](https://github.com/plotly/dash/pull/3987) Forward FastAPI reload scope options (`reload_dirs`, `reload_excludes`, and `reload_includes`) to Uvicorn when reloading.
- [#3986](https://github.com/plotly/dash/pull/3986) Adjust `_run_before_hooks` in the `fastapi` backend to honor a response returned by a `before_request` function, matching the `flask` backend's behavior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import DebugTitle from './DebugTitle';
import {SpinnerProps} from '../types';

/**
* Spinner created by Tobias Ahlin, https://github.com/tobiasahlin/SpinKit
* Spinner created by David Hu, https://github.com/davidhu2000/react-spinners
*/
const CircleSpinner = ({
status,
Expand All @@ -24,20 +24,7 @@ const CircleSpinner = ({
return (
<div style={style ? style : {}} className={spinnerClass}>
{debugTitle}
<div className="dash-spinner dash-sk-circle">
<div className="dash-sk-circle1 dash-sk-child" />
<div className="dash-sk-circle2 dash-sk-child" />
<div className="dash-sk-circle3 dash-sk-child" />
<div className="dash-sk-circle4 dash-sk-child" />
<div className="dash-sk-circle5 dash-sk-child" />
<div className="dash-sk-circle6 dash-sk-child" />
<div className="dash-sk-circle7 dash-sk-child" />
<div className="dash-sk-circle8 dash-sk-child" />
<div className="dash-sk-circle9 dash-sk-child" />
<div className="dash-sk-circle10 dash-sk-child" />
<div className="dash-sk-circle11 dash-sk-child" />
<div className="dash-sk-circle12 dash-sk-child" />
</div>
<div className="dash-spinner dash-circle-spinner" />
<style>
{`
.dash-spinner-container {
Expand All @@ -55,128 +42,29 @@ const CircleSpinner = ({
.dash-loading-title {
text-align: center;
}
.dash-sk-circle {
.dash-circle-spinner {
margin: 1rem auto;
width: 40px;
height: 40px;
position: relative;
}
.dash-sk-circle .dash-sk-child {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.dash-sk-circle .dash-sk-child:before {
content: '';
display: block;
margin: 0 auto;
width: 15%;
height: 15%;
background-color: ${color};
box-sizing: border-box;
border: 2px solid ${color};
border-bottom-color: transparent;
border-radius: 100%;
-webkit-animation: dash-sk-circleBounceDelay 1.2s infinite ease-in-out both;
animation: dash-sk-circleBounceDelay 1.2s infinite ease-in-out both;
animation: dash-circle-spinner-rotate 0.75s infinite linear both;
}
.dash-sk-circle .dash-sk-circle2 {
-webkit-transform: rotate(30deg);
-ms-transform: rotate(30deg);
transform: rotate(30deg); }
.dash-sk-circle .dash-sk-circle3 {
-webkit-transform: rotate(60deg);
-ms-transform: rotate(60deg);
transform: rotate(60deg); }
.dash-sk-circle .dash-sk-circle4 {
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg); }
.dash-sk-circle .dash-sk-circle5 {
-webkit-transform: rotate(120deg);
-ms-transform: rotate(120deg);
transform: rotate(120deg); }
.dash-sk-circle .dash-sk-circle6 {
-webkit-transform: rotate(150deg);
-ms-transform: rotate(150deg);
transform: rotate(150deg); }
.dash-sk-circle .dash-sk-circle7 {
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg); }
.dash-sk-circle .dash-sk-circle8 {
-webkit-transform: rotate(210deg);
-ms-transform: rotate(210deg);
transform: rotate(210deg); }
.dash-sk-circle .dash-sk-circle9 {
-webkit-transform: rotate(240deg);
-ms-transform: rotate(240deg);
transform: rotate(240deg); }
.dash-sk-circle .dash-sk-circle10 {
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg); }
.dash-sk-circle .dash-sk-circle11 {
-webkit-transform: rotate(300deg);
-ms-transform: rotate(300deg);
transform: rotate(300deg); }
.dash-sk-circle .dash-sk-circle12 {
-webkit-transform: rotate(330deg);
-ms-transform: rotate(330deg);
transform: rotate(330deg); }
.dash-sk-circle .dash-sk-circle2:before {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s; }
.dash-sk-circle .dash-sk-circle3:before {
-webkit-animation-delay: -1s;
animation-delay: -1s; }
.dash-sk-circle .dash-sk-circle4:before {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s; }
.dash-sk-circle .dash-sk-circle5:before {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s; }
.dash-sk-circle .dash-sk-circle6:before {
-webkit-animation-delay: -0.7s;
animation-delay: -0.7s; }
.dash-sk-circle .dash-sk-circle7:before {
-webkit-animation-delay: -0.6s;
animation-delay: -0.6s; }
.dash-sk-circle .dash-sk-circle8:before {
-webkit-animation-delay: -0.5s;
animation-delay: -0.5s; }
.dash-sk-circle .dash-sk-circle9:before {
-webkit-animation-delay: -0.4s;
animation-delay: -0.4s; }
.dash-sk-circle .dash-sk-circle10:before {
-webkit-animation-delay: -0.3s;
animation-delay: -0.3s; }
.dash-spinner-container > .sk-circle .sk-circle11:before {
-webkit-animation-delay: -0.2s;
animation-delay: -0.2s; }
.dash-spinner-container .sk-circle .sk-circle12:before {
-webkit-animation-delay: -0.1s;
animation-delay: -0.1s; }

@-webkit-keyframes dash-sk-circleBounceDelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1);
transform: scale(1);
@keyframes dash-circle-spinner-rotate {
0% {
transform: rotate(0deg) scale(1);
}
}

@keyframes dash-sk-circleBounceDelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1);
transform: scale(1);
50% {
transform: rotate(180deg) scale(0.8);
}
100% {
transform: rotate(360deg) scale(1);
}
}
`}
`}
</style>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -886,3 +886,28 @@ def updateDiv(n_clicks):
dash_dcc.wait_for_text_to_equal(".div-1", "changed")

assert dash_dcc.get_logs() == []


def test_ldcp020_loading_component_circle_spinner(dash_dcc):
app = Dash(__name__)

app.layout = html.Div(
dcc.Loading(
html.Div("content"),
type="circle",
color="rgb(255, 0, 0)",
display="show",
className="loading",
)
)

dash_dcc.start_server(app)

spinner = ".loading .dash-spinner.dash-circle-spinner"
dash_dcc.wait_for_style_to_equal(spinner, "border-top-color", "rgba(255, 0, 0, 1)")
dash_dcc.wait_for_style_to_equal(spinner, "border-bottom-color", "rgba(0, 0, 0, 0)")
dash_dcc.wait_for_style_to_equal(
spinner, "animation-name", "dash-circle-spinner-rotate"
)

assert dash_dcc.get_logs() == []
2 changes: 1 addition & 1 deletion tests/integration/renderer/test_loading_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def input_triggers_nested(value):

def wait_for_all_spinners():
dash_duo.find_element(".loading-1 .dash-spinner.dash-default-spinner")
dash_duo.find_element(".loading-2 .dash-spinner.dash-sk-circle")
dash_duo.find_element(".loading-2 .dash-spinner.dash-circle-spinner")
dash_duo.find_element(".loading-3 .dash-spinner.dash-cube-container")

def wait_for_no_spinners():
Expand Down