Skip to content

Commit

Permalink
chore: loading state for Button (#67)
Browse files Browse the repository at this point in the history
* feat: add loading spinner to Button

* feat: provide loading spinner & ARIA message for Button

* chore: simplify screen reader markup
  • Loading branch information
jaredcwhite authored Oct 24, 2023
1 parent 5224f64 commit a91301e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
39 changes: 39 additions & 0 deletions src/actions/Button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,43 @@
outline: var(--seeds-focus-ring-outline);
box-shadow: var(--seeds-focus-ring-box-shadow);
}

// loading indicator
&::after {
content: "";
position: absolute;
display: block;
width: 1.5em;
height: 1.5em;
top: calc(50% - .75em);
left: calc(50% - .75em);
border: 3px solid var(--seeds-color-on-inverse);
border-left-color: transparent;
border-radius: 50%;
opacity: 0;
transition: opacity 250ms;
}

&[data-loading="true"] {
position: relative;
color: transparent;

&::after {
opacity: 1;
animation: spinning-rotation 1s linear infinite;
}

.btnSubmit-text {
visibility: hidden;
}
}
}

@keyframes spinning-rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
7 changes: 6 additions & 1 deletion src/actions/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export interface ButtonProps {
ariaHidden?: boolean
/** Accessible label if button doesn't contain text content */
ariaLabel?: string
/** Show loading spinner and set ARIA live message while disabling clicks */
loadingMessage?: string | null
/** Element ID */
id?: string
/** Additional CSS classes */
Expand Down Expand Up @@ -77,11 +79,13 @@ const setupButtonProps = (props: ButtonProps) => {
updatedProps: {
"data-variant": props.variant || "primary",
"data-size": props.size || "md",
"data-loading": props.loadingMessage ? "true" : undefined,
id: props.id,
className: classNames.join(" "),
target: props.newWindowTarget ? "_blank" : undefined,
"aria-label": props.ariaLabel,
"aria-hidden": props.ariaHidden,
"aria-disabled": props.loadingMessage ? "true" : undefined,
tabIndex: props.ariaHidden ? -1 : null,
},
inner: {
Expand Down Expand Up @@ -132,10 +136,11 @@ const Button = (props: ButtonProps) => {
<ButtonElement
type={props.type || "button"}
disabled={props.disabled}
onClick={props.onClick}
onClick={props.loadingMessage ? (event) => event.preventDefault : props.onClick}
{...updatedProps}
>
{buttonInner}
<span className="seeds-screen-reader-only" aria-live="assertive">{props.loadingMessage}</span>
</ButtonElement>
)
}
Expand Down
8 changes: 8 additions & 0 deletions src/actions/__stories__/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,11 @@ export const textButtons = () => (

</>
)

export const loadingButton = () => {
const [loading, setLoading] = React.useState<null | string>(null)
return <Button loadingMessage={loading} onClick={() => {
setLoading("Saving form")
setTimeout(() => setLoading(null), 3000)
}}>Click to Spin</Button>
}

0 comments on commit a91301e

Please sign in to comment.