Skip to content
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

Merged
merged 8 commits into from
Jan 9, 2025
Merged

va-button: add loading variation #1446

merged 8 commits into from
Jan 9, 2025

Conversation

powellkerry
Copy link
Contributor

@powellkerry powellkerry commented Jan 7, 2025

Chromatic

https://3460-button-loading--65a6e2ed2314f7b8f98609d8.chromatic.com

Description

Add loading variation for button.
The button should:

  • act disabled (greyed out and disable all interactions)
  • display an animated loading icon, the icon should not be animated for reduced motion users
  • text should default to "Loading..." if the text prop is not set
  • Screen readers should announce "loading" when the loading prop is true and "Loading complete" when it is set to false

Closes 3460

QA Checklist

  • [ x] Component maintains 1:1 parity with design mocks
  • [ x] Text is consistent with what's been provided in the mocks
  • [ x] Component behaves as expected across breakpoints
  • Accessibility expert has signed off on code changes (if applicable. If not applicable provide reason why)
  • Designer has signed off on changes (if applicable. If not applicable provide reason why)
  • [ x] Tab order and focus state work as expected
  • [ x] Changes have been tested against screen readers (if applicable. If not applicable provide reason why)
  • [ x] New components are covered by e2e tests; updates to existing components are covered by existing test suite
  • [ x] Changes have been tested in vets-website using Verdaccio (if applicable. If not applicable provide reason why)

Screenshots

Screenshot 2025-01-07 at 9 01 21 AM

Acceptance criteria

  • QA checklist has been completed
  • [ x] Screenshots have been attached that cover desktop and mobile screens

Definition of done

  • [ x] Documentation has been updated, if applicable
  • [ x] A link has been provided to the originating GitHub issue (or connected to it via ZenHub)

@powellkerry powellkerry added the minor For a minor Semantic Version change label Jan 7, 2025
@powellkerry powellkerry requested a review from a team as a code owner January 7, 2025 16:03
{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*/}
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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>
)}

Copy link
Contributor

@danbrady danbrady left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design looks good. That animation is 🔥!

Copy link
Contributor

@jamigibbs jamigibbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking that I could see but I had just a few questions and comments. The animation sure is slick. 😎

@@ -50,6 +53,22 @@ export class VaButton {
*/
@Prop({ reflect: true }) disabled?: boolean = false;

/**
* If `true`, the click event will not fire.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this description accurate for the loading prop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is handled the same way as the disabled prop by preventDefault() and preventPropogation() on line 168.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ok yes that makes sense. I was expecting it to mention that it will display the loading indicator too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I should add that! Thanks!

animation: none;
}

@media (prefers-reduced-motion) {
Copy link
Contributor

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!

{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*/}
Copy link
Contributor

Choose a reason for hiding this comment

The 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>
)}

@Watch('loading')
announceLoadingChange(newValue: boolean, oldValue: boolean) {
if (oldValue && !newValue) {
var me = this;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this variable assignment be let?

this.showCompletedMessage = true;
setTimeout(() => {
me.showCompletedMessage = false;
}, 3000);
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <span role="status"> will only be read by the screen reader if there are changes to the content of the span. This is the reasoning for the span always being rendered, even if the loading prop is false, because if we render it when the prop changes the reader will not count that as a change and "Loading" will never be read.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Thanks for the explanation!

Copy link
Contributor

@rsmithadhoc rsmithadhoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looked good in my testing, screen readers announced the loading state/complete as expected. Thanks!

aria-disabled={ariaDisabled}
aria-busy={loading ? 'true' : undefined}
aria-label={label}
aria-live={loading ? 'polite' : 'off'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe we'll need this aria-live on the button itself, as the span will handle the live region. Leaving it in doesn't seem to cause any negative effects, but could lead to repetitive announcements. I tested this code without it and it announced as expected.

@powellkerry powellkerry merged commit 1bbb3a0 into main Jan 9, 2025
8 checks passed
@powellkerry powellkerry deleted the 3460-button-loading branch January 9, 2025 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor For a minor Semantic Version change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add loading disabled button variation - development
5 participants