Skip to content

Commit c9841f2

Browse files
committed
fix: update API for component
1 parent 8da41df commit c9841f2

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

site/src/components/Spinner/Spinner.tsx

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import isChromatic from "chromatic/isChromatic";
88
import { type VariantProps, cva } from "class-variance-authority";
9-
import type { FC, ReactNode } from "react";
9+
import { type FC, useEffect, useState } from "react";
1010
import { cn } from "utils/cn";
1111

1212
const SPINNER_LEAF_COUNT = 8;
@@ -26,35 +26,62 @@ const spinnerVariants = cva("", {
2626
type SpinnerProps = Readonly<
2727
React.SVGProps<SVGSVGElement> &
2828
VariantProps<typeof spinnerVariants> & {
29-
children?: ReactNode;
30-
loading?: boolean;
29+
loading: boolean;
3130
unmountedWhileLoading?: boolean;
3231
spinnerStartDelayMs?: number;
3332
}
3433
>;
3534

3635
const leavesIterable = Array.from({ length: SPINNER_LEAF_COUNT }, (_, i) => i);
37-
3836
export const Spinner: FC<SpinnerProps> = ({
3937
className,
4038
size,
4139
loading,
4240
children,
43-
...props
41+
spinnerStartDelayMs = 0,
42+
unmountedWhileLoading = false,
43+
...delegatedProps
4444
}) => {
45-
if (!loading) {
45+
// Doing some mid-render state syncs to minimize re-renders and risks of
46+
// contradictory states. It's ugly, but it's what the React team recommends
47+
const noDelay = spinnerStartDelayMs === 0;
48+
const [mountSpinner, setMountSpinner] = useState(noDelay);
49+
const unmountImmediatelyOnRerender = mountSpinner && !loading;
50+
if (unmountImmediatelyOnRerender) {
51+
setMountSpinner(false);
52+
}
53+
const mountImmediatelyOnRerender = !mountSpinner && noDelay;
54+
if (mountImmediatelyOnRerender) {
55+
setMountSpinner(true);
56+
}
57+
useEffect(() => {
58+
if (spinnerStartDelayMs === 0) {
59+
return;
60+
}
61+
62+
const delayId = window.setTimeout(() => {
63+
setMountSpinner(true);
64+
}, spinnerStartDelayMs);
65+
return () => window.clearTimeout(delayId);
66+
}, [spinnerStartDelayMs]);
67+
68+
// Past this point, only showSpinner should need to be referenced
69+
const showSpinner = loading && mountSpinner;
70+
if (!showSpinner) {
4671
return children;
4772
}
4873

4974
return (
5075
<svg
76+
// Fill is the only prop that should be allowed to be overridden;
77+
// all other props must come after destructuring
78+
fill="currentColor"
79+
{...delegatedProps}
5180
viewBox="0 0 24 24"
5281
xmlns="http://www.w3.org/2000/svg"
53-
fill="currentColor"
5482
className={cn(className, spinnerVariants({ size }))}
55-
{...props}
5683
>
57-
<title>Loading spinner</title>
84+
<title>Loading&hellip;</title>
5885
{leavesIterable.map((leafIndex) => (
5986
<rect
6087
key={leafIndex}

site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export const CreateOrganizationPageView: FC<
155155
</fieldset>
156156
<div className="flex flex-row gap-2">
157157
<Button type="submit" disabled={form.isSubmitting}>
158-
{form.isSubmitting && <Spinner />}
158+
<Spinner loading={form.isSubmitting} />
159159
Save
160160
</Button>
161161
<Button

0 commit comments

Comments
 (0)