Skip to content

Commit 638ccf5

Browse files
committed
fix: make first effort towards preserving state when content for loading transitions
1 parent fc44cdb commit 638ccf5

File tree

1 file changed

+47
-42
lines changed

1 file changed

+47
-42
lines changed

site/src/components/Spinner/Spinner.tsx

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -82,52 +82,57 @@ export const Spinner: FC<SpinnerProps> = ({
8282
unmountChildrenWhileLoading = false,
8383
...delegatedProps
8484
}) => {
85-
/**
86-
* @todo Figure out if this conditional logic can ever cause a component to
87-
* lose state when showSpinner flips from false to true while
88-
* unmountedWhileLoading is false. I would hope not, since the children prop
89-
* is the same in both cases, but I need to test this out
90-
*/
85+
// Conditional rendering logic is more convoluted than normal because we
86+
// need to make sure that the children prop is always placed in the same JSX
87+
// "slot" by default, no matter the value of `loading`. Even if the children
88+
// prop value is exactly the same each time, the state will get wiped if the
89+
// placement in the JSX output changes
9190
const showSpinner = useShowSpinner(loading, spinnerDelayMs);
92-
if (!showSpinner) {
93-
return children;
94-
}
95-
9691
return (
9792
<>
98-
<svg
99-
// Fill is the only prop that should be allowed to be
100-
// overridden; all other props must come after destructuring
101-
fill="currentColor"
102-
{...delegatedProps}
103-
viewBox="0 0 24 24"
104-
xmlns="http://www.w3.org/2000/svg"
105-
className={cn(className, spinnerVariants({ size }))}
106-
>
107-
<title>Loading&hellip;</title>
108-
{leafIndices.map((index) => (
109-
<rect
110-
key={index}
111-
x="10.9"
112-
y="2"
113-
width="2"
114-
height="5.5"
115-
rx="1"
116-
style={{
117-
...animationSettings,
118-
transform: `rotate(${index * (360 / SPINNER_LEAF_COUNT)}deg)`,
119-
transformOrigin: "center",
120-
animationDelay: `${-index * 0.1}s`,
121-
}}
122-
/>
123-
))}
124-
</svg>
93+
{showSpinner && (
94+
<svg
95+
// `fill` is the only prop that should be allowed to be
96+
// overridden; all other props must come after destructuring
97+
fill="currentColor"
98+
{...delegatedProps}
99+
viewBox="0 0 24 24"
100+
xmlns="http://www.w3.org/2000/svg"
101+
className={cn(className, spinnerVariants({ size }))}
102+
>
103+
<title>Loading&hellip;</title>
104+
{leafIndices.map((index) => (
105+
<rect
106+
key={index}
107+
x="10.9"
108+
y="2"
109+
width="2"
110+
height="5.5"
111+
rx="1"
112+
style={{
113+
...animationSettings,
114+
transform: `rotate(${index * (360 / SPINNER_LEAF_COUNT)}deg)`,
115+
transformOrigin: "center",
116+
animationDelay: `${-index * 0.1}s`,
117+
}}
118+
/>
119+
))}
120+
</svg>
121+
)}
125122

126-
{!unmountChildrenWhileLoading && (
127-
<div className="sr-only">
128-
This content is loading:
129-
{children}
130-
</div>
123+
{/*
124+
* Invert the condition (showSpinner && unmountChildrenWhileLoading)
125+
* (which is the only one that should result in fully-unmounted
126+
* content), and then if we still get content, handle the other
127+
* three cases of the boolean truth table more granularly
128+
*/}
129+
{(!showSpinner || !unmountChildrenWhileLoading) && (
130+
<>
131+
<span className={showSpinner ? "sr-only" : "hidden"}>
132+
This content is loading:{" "}
133+
</span>
134+
<span className={showSpinner ? "sr-only" : "inline"}>{children}</span>
135+
</>
131136
)}
132137
</>
133138
);

0 commit comments

Comments
 (0)