Skip to content

Commit f06fbb0

Browse files
authored
fix!: CSS based loading spinner (#1532)
- Swapped our LoadingSpinner implementation with a simpler html / CSS only spinner - Fixes a layout shift after font load caused by `font-size: $iris-panel-icon-font-size` in `.message-icon` of LoadingOverlay - Fixes loading spinner backtracking due to multiple instances of spinners **Regression Tests Performed** - LoadingOverlay - I added a synthetic wait to PluginUtils right after the manifest loading. Loading overlay is now seamless. No layout shift or backtracking Verified the following look as they did before Community - Styleguide indeterminate and progress spinners - Styleguide context menu spinners - Console "Running" spinner - AppMainContainer disconnection spinner - Command history tooltip elapsed spinner - Advanced filter spinner - Column statistics spinner - IrisGridCopyHandler slight adjustment such that spinner + text are centered - PartitionSelectorSearch spinner - Commit button in PendingDataBottomBar - Custom column builder "applying" spinner - Table CSV exporter spinner Enterprise - Enterprise login spinner ``` await new Promise(resolve => { setTimeout(resolve, 2000); }); ``` fixes #1531 BREAKING CHANGE: Inline LoadingSpinner instances will need to be decorated with `className="loading-spinner-vertical-align"` for vertical alignment to work as before
1 parent 49723ec commit f06fbb0

31 files changed

Lines changed: 267 additions & 129 deletions

jest.setup.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ Object.defineProperty(window, 'matchMedia', {
3333
})),
3434
});
3535

36+
Object.defineProperty(window, 'CSSAnimation', {
37+
value: function () {
38+
return class CSSAnimation {};
39+
},
40+
});
41+
3642
Object.defineProperty(window, 'performance', {
3743
value: performance,
3844
writable: true,
@@ -57,3 +63,8 @@ Object.defineProperty(document, 'fonts', {
5763
ready: Promise.resolve(),
5864
},
5965
});
66+
67+
Object.defineProperty(document, 'getAnimations', {
68+
value: () => [],
69+
writable: true,
70+
});

packages/auth-plugins/src/LoginForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function LoginForm({
4949
>
5050
{isLoggingIn && (
5151
<span>
52-
<LoadingSpinner />
52+
<LoadingSpinner className="loading-spinner-vertical-align" />
5353
<span className="btn-normal-content">Logging in</span>
5454
<span className="btn-hover-content">Cancel</span>
5555
</span>

packages/code-studio/src/styleguide/Progress.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function Progress(): React.ReactElement {
3434
onClick={() => undefined}
3535
>
3636
<span>
37-
<LoadingSpinner />
37+
<LoadingSpinner className="loading-spinner-vertical-align" />
3838
<span className="btn-normal-content">Connecting</span>
3939
<span className="btn-hover-content">Cancel</span>
4040
</span>

packages/code-studio/src/styleguide/Tooltips.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function Tooltips(): React.ReactElement {
4646
<hr />
4747
<div>And some icons down here</div>
4848
<div>
49-
<LoadingSpinner />
49+
<LoadingSpinner className="loading-spinner-vertical-align" />
5050
{iconElements}
5151
</div>
5252
</Tooltip>

packages/components/scss/BaseStyleSheet.scss

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ span.btn-disabled-wrapper {
174174

175175
.btn-spinner {
176176
padding: $btn-padding-y 1rem;
177-
.fa-layers {
177+
.fa-layers,
178+
.loading-spinner {
178179
margin-right: 0.5rem;
179180
}
180181
}
@@ -255,7 +256,9 @@ span.btn-disabled-wrapper {
255256
border-radius: 2px;
256257
margin-bottom: 2px;
257258
filter: saturate(0%);
258-
transition: filter 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
259+
transition:
260+
filter 0.15s ease-in-out,
261+
box-shadow 0.15s ease-in-out;
259262
pointer-events: none;
260263
}
261264

@@ -712,7 +715,8 @@ input[type='number']::-webkit-inner-spin-button {
712715

713716
.monaco-checkbox:focus {
714717
border: none !important;
715-
box-shadow: $input-btn-focus-box-shadow,
718+
box-shadow:
719+
$input-btn-focus-box-shadow,
716720
0 0 0 1px $input-focus-border-color; //can't use regular border due to position of checkbox
717721
}
718722
}
@@ -746,7 +750,9 @@ input[type='number']::-webkit-inner-spin-button {
746750
linear-gradient(0deg, $input-bg, $input-bg);
747751
background-size: $custom-select-bg-size, cover;
748752
background-repeat: no-repeat;
749-
background-position: bottom 50% right $custom-select-padding-x, center;
753+
background-position:
754+
bottom 50% right $custom-select-padding-x,
755+
center;
750756
//make the dotted duplicate focus line on firefox go away
751757
&:-moz-focusring {
752758
color: rgba(0, 0, 0, 0%);
@@ -835,11 +841,18 @@ input[type='number']::-webkit-inner-spin-button {
835841
/// used by marching ants animation
836842
@keyframes march {
837843
from {
838-
background-position: 0 top, 0 bottom, left 0, right 0;
844+
background-position:
845+
0 top,
846+
0 bottom,
847+
left 0,
848+
right 0;
839849
}
840850

841851
to {
842-
background-position: $ant-size top, $ant-size bottom, left $ant-size,
852+
background-position:
853+
$ant-size top,
854+
$ant-size bottom,
855+
left $ant-size,
843856
right $ant-size;
844857
}
845858
}

packages/components/scss/bootstrap_overrides.scss

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,15 @@ $theme-color-interval: 9%;
9595
$yiq-contrasted-threshold: 180;
9696

9797
// Override fonts
98-
$font-family-sans-serif: 'Fira Sans', -apple-system, blinkmacsystemfont,
99-
'Segoe UI', 'Roboto', 'Helvetica Neue', arial, sans-serif; //fira sans then native system ui fallbacks
98+
$font-family-sans-serif:
99+
'Fira Sans',
100+
-apple-system,
101+
blinkmacsystemfont,
102+
'Segoe UI',
103+
'Roboto',
104+
'Helvetica Neue',
105+
arial,
106+
sans-serif; //fira sans then native system ui fallbacks
100107
$font-family-monospace: 'Fira Mono', menlo, monaco, consolas, 'Liberation Mono',
101108
'Courier New', monospace;
102109
$font-family-base: $font-family-sans-serif;
@@ -126,8 +133,11 @@ $box-shadow-900: 0 0.1rem 1rem rgba(0, 0, 0, 45%); //darkest shadow for $black p
126133
//Override Btn
127134
$btn-border-radius: 4rem;
128135
$btn-padding-x: 1.5rem;
129-
$btn-transition: color 0.12s ease-in-out, background-color 0.12s ease-in-out,
130-
border-color 0.12s ease-in-out, box-shadow 0.12s ease-in-out; //default 0.15 is too long
136+
$btn-transition:
137+
color 0.12s ease-in-out,
138+
background-color 0.12s ease-in-out,
139+
border-color 0.12s ease-in-out,
140+
box-shadow 0.12s ease-in-out; //default 0.15 is too long
131141
$btn-border-width: 2px;
132142

133143
//Override Inputs

packages/components/scss/new_variables.scss

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,16 @@ $ant-thickness: 1px;
2020
linear-gradient(to right, $color-2 50%, $color-1 50%),
2121
linear-gradient(to bottom, $color-2 50%, $color-1 50%),
2222
linear-gradient(to bottom, $color-2 50%, $color-1 50%);
23-
background-size: $ant-size $ant-thickness, $ant-size $ant-thickness,
24-
$ant-thickness $ant-size, $ant-thickness $ant-size;
25-
background-position: 0 top, 0 bottom, left 0, right 0;
23+
background-size:
24+
$ant-size $ant-thickness,
25+
$ant-size $ant-thickness,
26+
$ant-thickness $ant-size,
27+
$ant-thickness $ant-size;
28+
background-position:
29+
0 top,
30+
0 bottom,
31+
left 0,
32+
right 0;
2633
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
2734
animation: march 0.5s;
2835
animation-timing-function: linear;

packages/components/src/LoadingOverlay.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ $iris-panel-scrim-bg: rgba(black, 0.1);
1313
overflow: hidden;
1414

1515
.message-content {
16+
display: flex;
17+
flex-direction: column;
1618
font-size: $iris-panel-message-font-size;
19+
gap: 20px;
1720
.message-icon {
1821
font-size: $iris-panel-icon-font-size;
22+
height: $iris-panel-icon-font-size;
23+
line-height: $iris-panel-icon-font-size;
1924
.svg-inline--fa {
2025
font-size: inherit;
2126
}

packages/components/src/LoadingOverlay.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function LoadingOverlay({
2727
dataTestId != null ? `${dataTestId}-message` : undefined;
2828
const spinnerTestId =
2929
dataTestId != null ? `${dataTestId}-spinner` : undefined;
30+
3031
return (
3132
<CSSTransition
3233
in={Boolean(errorMessage) || !isLoaded || isLoading}
@@ -45,7 +46,12 @@ function LoadingOverlay({
4546
>
4647
<div className="message-content">
4748
<div className="message-icon">
48-
{isLoading && <LoadingSpinner data-testid={spinnerTestId} />}
49+
{isLoading && (
50+
<LoadingSpinner
51+
className="loading-spinner-large"
52+
data-testid={spinnerTestId}
53+
/>
54+
)}
4955
{!isLoading && errorMessage != null && (
5056
<FontAwesomeIcon icon={vsWarning} />
5157
)}
Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,56 @@
1+
/* stylelint-disable alpha-value-notation */
2+
.loading-spinner {
3+
--primary-color: var(
4+
--dh-loading-spinner-primary-color,
5+
var(--dh-accent-color, #4c7dee)
6+
);
7+
--secondary-color: var(
8+
--dh-loading-spinner-secondary-color,
9+
rgba(240, 240, 240, 0.5)
10+
);
11+
--border-width: 1px;
12+
--size: 14px;
13+
14+
box-sizing: border-box;
15+
display: inline-block;
16+
margin: 0 auto;
17+
width: var(--size);
18+
height: var(--size);
19+
}
20+
121
.loading-spinner-large {
2-
font-size: 64px;
3-
.svg-inline--fa {
4-
font-size: inherit;
22+
--border-width: 4px;
23+
--size: 56px;
24+
}
25+
26+
.loading-spinner-vertical-align {
27+
// The original LoadingSpinner used `.fa-layers` to create the spinner icon.
28+
// This includes a vertical aligment adjustment to center the spinner along
29+
// side of other inline content. Copying this value from the `.fa-layers`
30+
// class to avoid breaking alignment of the new spinner.
31+
vertical-align: -0.125em;
32+
}
33+
34+
// Spinning circle with colored border and transparent center. Half of the
35+
// circle is the primary color. The other half is the secondary color.
36+
.loading-spinner::after {
37+
animation: loading-spinner-rotate 2s linear infinite;
38+
border: var(--border-width) solid;
39+
border-color: var(--primary-color) var(--primary-color) var(--secondary-color)
40+
var(--secondary-color);
41+
border-radius: 50%;
42+
box-sizing: border-box;
43+
content: '';
44+
display: inline-block;
45+
width: var(--size);
46+
height: var(--size);
47+
}
48+
49+
@keyframes loading-spinner-rotate {
50+
0% {
51+
transform: rotate(0deg);
52+
}
53+
100% {
54+
transform: rotate(359deg);
555
}
656
}

0 commit comments

Comments
 (0)