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

USWDS - Character count: Enhance visual cue when limit is exceeded #5908

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from

Conversation

mahoneycm
Copy link
Contributor

@mahoneycm mahoneycm commented May 1, 2024

Summary

Enhanced visual cue when maxlength is exceeded. Now, the visual state of the character count will reflect its validation state.

Breaking change

This is not a breaking change.

Related issue

Closes #5851

Related pull requests

Changelog →

Preview link

Character count →

Problem statement

Testing results show that users expect additional indication that they have exceeded the limit. Additionally, screen magnification users may be unable to see the character count status message beneath the textarea input.

Solution

Add additional visual cues to let users know they have exceeded the character count limit.

Major changes

  • When users surpass the character limit, the usa-input--error class is added to the form input
    • This class will be removed when count is beneath limit
  • If users are over the character limit and remove focus from the input element, usa-input--error and usa-form-group--error are added to their relevant elements.
    • Once the input is beneath the count limit, removing focus will appropriately remove these classes

Testing and review

Character count behaviors

  1. Visit character component story →
  2. For both input variants, surpass relevant character limits.
  3. Confirm input error class and styles are applied correctly
  4. focusout each component
  5. Confirm label and form group error classes and styles are applied correctly
  6. Return to the input and backspace input to validate character count
  7. Confirm input error class and styles are removed
  8. focusouteach component
  9. Confirm label and form group error classes and styles are removed

Testing checklist

  • npm run test runs without error
  • usa-input--errorclasses are applied on character input over character limit
  • usa-input--errorclasses are removed on character input under character limit
  • usa-form-group--error classes are applied after focusout of over limit input
  • usa-form-group--error classes are removed after focusout of under limit input
  • usa-label--error classes are applied after focusout of over limit input
  • usa-label--error classes are removed after focusout of under limit input
  • Character count JS matches USWDS code standards.
  • New unit tests match USWDS code standards

Copy link
Contributor Author

@mahoneycm mahoneycm left a comment

Choose a reason for hiding this comment

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

Due to the unit test updates, there are a handful of changed files with this PR. Added some comments to help add clarity to each of the changes:

Comment on lines +38 to +49
const formGroupEl = characterCountEl.querySelector(FORM_GROUP);

if (!formGroupEl) {
throw new Error(`${CHARACTER_COUNT} is missing inner ${FORM_GROUP}`);
}

const labelEl = characterCountEl.querySelector(LABEL);

if (!labelEl) {
throw new Error(`${CHARACTER_COUNT} is missing inner ${LABEL}`);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

Added checks for form group and label elements. These elements are necessary for the overall structure of the component and will result in JS errors for toggleErrorState if they are missing.

Comment on lines +176 to +190
/**
* Updates character count form elements to match error status of input.
*
* @description On focusout, it will update the input label and form group to match the current state of the input element.
* Called on focusout instead of input to prevent content shift while typing.
* @param {HTMLInputElement|HTMLTextAreaElement} inputEl The character count input element
*/
const toggleErrorState = (inputEl) => {
const { labelEl, formGroupEl } = getCharacterCountElements(inputEl);
const errorState = inputEl.classList.contains(INPUT_ERROR_CLASS);

labelEl.classList.toggle(LABEL_ERROR_CLASS, errorState);
formGroupEl.classList.toggle(FORM_GROUP_ERROR_CLASS, errorState);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

toggleErrorState adds/removes form-group and usa-label with relevant --error classes. Called on focusout

Comment on lines +8 to +13
const FORM_GROUP_CLASS = `${PREFIX}-form-group`;
const FORM_GROUP_ERROR_CLASS = `${FORM_GROUP_CLASS}--error`;
const FORM_GROUP = `.${FORM_GROUP_CLASS}`;
const LABEL_CLASS = `${PREFIX}-label`;
const LABEL_ERROR_CLASS = `${LABEL_CLASS}--error`;
const LABEL = `.${LABEL_CLASS}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

Added classes and element used by toggleErrorState. Organized by when they appear in markup/DOM

Comment on lines +226 to +228
FORM_GROUP_ERROR_CLASS,
LABEL_ERROR_CLASS,
INPUT_ERROR_CLASS,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

Exported for use in new unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated unit test templates

I added usa-form-group and usa-label's to all character count unit tests for consistency and to prevent JS errors now that the component checks for these elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

New unit test to check for the existence of form group element

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

New unit test to check for the existence of the label element

Comment on lines +167 to +191
it("should add form group error classes on focusout when the input has error class", () => {
input.classList.add(INPUT_ERROR_CLASS);

EVENTS.focusout(input);

assert.strictEqual(
formGroup.classList.contains(FORM_GROUP_ERROR_CLASS),
true
);
assert.strictEqual(label.classList.contains(LABEL_ERROR_CLASS), true);
});

it("should remove form group error classes on focusout when the input does not have error class", () => {
assert.strictEqual(input.classList.contains(INPUT_ERROR_CLASS), false);
formGroup.classList.add(FORM_GROUP_ERROR_CLASS);
label.classList.add(LABEL_ERROR_CLASS);

EVENTS.focusout(input);

assert.strictEqual(
formGroup.classList.contains(FORM_GROUP_ERROR_CLASS),
false
);
assert.strictEqual(label.classList.contains(LABEL_ERROR_CLASS), false);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

New testing requirements to check for the addition / removal of error states on input and focusout

Comment on lines +216 to +220
focusout: {
[INPUT]() {
toggleErrorState(this);
},
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add form group error classes on focusout

This idea came from a discussion between @jaclinec and I. We wanted to add the full error state classes but didn't want to have the content shift while the user was typing. She suggested we add these classes only after the user leaves the field.

Copy link
Contributor

@amyleadem amyleadem May 22, 2024

Choose a reason for hiding this comment

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

I think this approach makes sense. One thing I noticed is that it didn't quite reverse the way I expected when when I returned to the input and resolved the error. (See screenshot below).

To replicate:

  1. Turn on the input error state by entering too many characters
  2. Move focus out of the input, which activates the form group error styles
  3. Return focus to the input
  4. Remove enough characters so that it no longer exceeds maxlength
  5. Keep focus on the input and notice it still shows an error state, even though there is no error

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@amyleadem this is the intentional behavior based on @jaclinec's recommendation to not cause content shift while the user is typing (see comment above and testing instructions)

If the content shift on removal is acceptable, I can make it so that the error state is immediately removed rather than waiting for focusout.

Here's a quick example of adding/removing the classes on input. I think this would be particularly jarring for screen magnification users

Kapture.2024-05-23.at.10.02.35.webm

@@ -150,9 +169,25 @@ const updateCountMessage = (inputEl) => {
inputEl.setCustomValidity("");
}

inputEl.classList.toggle(INPUT_ERROR_CLASS, isOverLimit);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

Adds / removes input error classes to match invalid status message class

@mahoneycm mahoneycm requested review from mejiaj, amyleadem, jaclinec and amycole501 and removed request for amycole501 May 2, 2024 15:24
@mahoneycm mahoneycm marked this pull request as ready for review May 2, 2024 15:24
@amycole501
Copy link

@mahoneycm we think there might be an issue with the blue focus indicator and the red indicator not having enough contrast with one another. Is there a way to add a small space between the two (maybe a few pixels) to make a distinction there are two states happening (one focus, one error)? https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html

@mahoneycm
Copy link
Contributor Author

@amycole501 Here's what it looks like it with an offset of 2px

image
Kapture.2024-05-03.at.15.59.41.webm

The input shrinks a bit but this is actually a result of the error border being added and not the outline offset. The same difference in input size occurs with or without the offset.

What do we think about something like this?

This issue might be a bit out of scope for this specific issue but definitely something we should consider with #5884

CC: @jaclinec @mejiaj @amyleadem

@amycole501
Copy link

I like it! Would like to know what @alex-hull thinks but the separation makes it much easier to see the two borders.

Copy link
Contributor

@amyleadem amyleadem left a comment

Choose a reason for hiding this comment

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

@mahoneycm
I started testing the interactions on this and generally it looks good! I had one question below about what happens after you fix an error (shown in the comment below). Let me know if you want to discuss!

I'll return to the code review once we figure that out.

Comment on lines +216 to +220
focusout: {
[INPUT]() {
toggleErrorState(this);
},
},
Copy link
Contributor

@amyleadem amyleadem May 22, 2024

Choose a reason for hiding this comment

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

I think this approach makes sense. One thing I noticed is that it didn't quite reverse the way I expected when when I returned to the input and resolved the error. (See screenshot below).

To replicate:

  1. Turn on the input error state by entering too many characters
  2. Move focus out of the input, which activates the form group error styles
  3. Return focus to the input
  4. Remove enough characters so that it no longer exceeds maxlength
  5. Keep focus on the input and notice it still shows an error state, even though there is no error

image

@mahoneycm mahoneycm requested a review from amyleadem May 23, 2024 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

USWDS - Character count: Implement a visual cue when the limit is exceeded
3 participants