-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
va-button: add loading variation #1446
Changes from 6 commits
db8bc4b
42b42b5
f190471
998d664
ef7bff5
47be963
99f0fe1
937adff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import { | |
Listen, | ||
Prop, | ||
Element, | ||
Watch, | ||
} from '@stencil/core'; | ||
import classnames from 'classnames'; | ||
|
||
|
@@ -23,6 +24,8 @@ import classnames from 'classnames'; | |
shadow: true, | ||
}) | ||
export class VaButton { | ||
private showCompletedMessage: boolean = false; | ||
|
||
@Element() el: HTMLElement; | ||
|
||
/** | ||
|
@@ -50,6 +53,22 @@ export class VaButton { | |
*/ | ||
@Prop({ reflect: true }) disabled?: boolean = false; | ||
|
||
/** | ||
* If `true`, the button will appear disabled, a loading icon will show next to the text, and the click event will not fire. | ||
*/ | ||
@Prop({ reflect: true }) loading?: boolean = false; | ||
|
||
@Watch('loading') | ||
announceLoadingChange(newValue: boolean, oldValue: boolean) { | ||
if (oldValue && !newValue) { | ||
let me = this; | ||
this.showCompletedMessage = true; | ||
setTimeout(() => { | ||
me.showCompletedMessage = false; | ||
}, 3000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand the need for a timeout and why this specific time is used. Do you mind clarifying? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The same applies to this timeout. If there is not any time between when the completion message is set and when it is set to null, the reader won't detect the change and the message won't be read. Its an arbitrary number to guarantee the "Loading complete" is read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense. Thanks for the explanation! |
||
} | ||
} | ||
|
||
/** | ||
* The aria-label of the component. | ||
*/ | ||
|
@@ -111,7 +130,7 @@ export class VaButton { | |
private getButtonText = (): string => { | ||
if (this.continue) return 'Continue'; | ||
if (this.back) return 'Back'; | ||
|
||
if (this.loading && !this.text) return 'Loading...'; | ||
return this.text; | ||
}; | ||
|
||
|
@@ -145,7 +164,7 @@ export class VaButton { | |
*/ | ||
@Listen('click') | ||
handleClickOverride(e: MouseEvent) { | ||
if (this.disabled) { | ||
if (this.disabled || this.loading) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
return; | ||
|
@@ -159,6 +178,7 @@ export class VaButton { | |
back, | ||
continue: _continue, | ||
disabled, | ||
loading, | ||
getButtonText, | ||
label, | ||
submit, | ||
|
@@ -171,7 +191,7 @@ export class VaButton { | |
const ariaDescribedbyIds = | ||
`${messageAriaDescribedby ? 'button-description' : ''}`.trim() || null; | ||
|
||
const ariaDisabled = disabled ? 'true' : undefined; | ||
const ariaDisabled = disabled || loading ? 'true' : undefined; | ||
const buttonText = getButtonText(); | ||
|
||
const type = submit !== undefined ? 'submit' : 'button'; | ||
|
@@ -185,18 +205,27 @@ export class VaButton { | |
|
||
return ( | ||
<Host> | ||
<button | ||
class={buttonClass} | ||
aria-disabled={ariaDisabled} | ||
aria-label={label} | ||
aria-describedby={ariaDescribedbyIds} | ||
type={type} | ||
part="button" | ||
> | ||
{back && !_continue && <va-icon icon="navigate_far_before" />} | ||
{buttonText} | ||
{_continue && !back && <va-icon icon="navigate_far_next" />} | ||
</button> | ||
{/* This span must always be present for changes to be announced for the loading prop. It will not show visually or be read without content*/} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love the idea of this span being rendered for all buttons. I have tested it and it does not announce anything when there is not content, but it would be nice to figure out a way to only render it if the button will be used for loading. Hoping someone has an idea for how to do that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's a big deal to have a span always in the markup but was there some limitation with just doing conditional JSX? Something like: {this.loading && (
<span class="loading-message" role="status">....</span>
)} |
||
<span class="loading-message" role="status"> | ||
{this.loading ? 'Loading' : this.showCompletedMessage ? 'Loading complete' : null} | ||
</span> | ||
<button | ||
class={buttonClass} | ||
aria-disabled={ariaDisabled} | ||
aria-busy={loading ? 'true' : undefined} | ||
aria-label={label} | ||
aria-live={loading ? 'polite' : 'off'} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe we'll need this |
||
aria-describedby={ariaDescribedbyIds} | ||
type={type} | ||
part="button" | ||
> | ||
{back && !_continue && <va-icon icon="navigate_far_before" />} | ||
{ | ||
loading ? <va-icon class="loading-icon" icon="autorenew" aria-hidden="true"/> : null | ||
} | ||
{buttonText} | ||
{_continue && !back && <va-icon icon="navigate_far_next" />} | ||
</button> | ||
{messageAriaDescribedby && ( | ||
<span id="button-description" class="usa-sr-only"> | ||
{messageAriaDescribedby} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a cool media feature. TIL!