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

FEI-5533: Re-enable select keyboard tests for Dropdown and Clickable #2420

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

marcysutton
Copy link
Member

@marcysutton marcysutton commented Jan 7, 2025

In working on WB-1799 for SingleSelect, I found a bunch of disabled tests that I started fixing to ensure keyboard interactions work. Fixing these tests required changes to Clickable, which is used in WB Dropdowns.

Jira issue: https://khanacademy.atlassian.net/browse/FEI-5533

PR highlights:

  1. Re-enables some keyboard tests that were disabled with userEvent@14
  2. Exposes a centralized keys object in wonder-blocks-core for use in wonder-blocks-clickable, wonder-blocks-dropdown, and any other modules that need it.
    1. Note: userEvent passes the literal key code string back, i.e. Space or space. I lower-cased the key names to make sure they match in the code. We could use linting to enforce this instead... I'm open to suggestions.
  3. Keeps two tests for SingleSelect disabled:
    1. "It should find and select an item using the keyboard": fixing this one broke other tests (the dropdown isn't open before typing so refs aren't populated, but explicitly opening it made filtering tests fail).
    2. "It should change the number of options after using the search filter" (Live Region test): this one fails for me when I enable it. I'll plan to revisit it as part of the Announcer implementation.

Test Plan

Run the tests using yarn test.

There is one pre-existing lint failure for me locally in expect-render-error.d.ts, but it seems unrelated to these changes.

userEvent@14 changed how keyboard events are handled, so they were failing to match things like `" "` or `32` for space key codes. `keyboard('{space}')` passes back a case-sensitive event name that must be matched specifically, like "Space" or "space". To make this work in ClickableBehavior, I centralized the keys into wonder-blocks-core so it could be reused in Dropdowns and elsewhere.

I also included a story with one of the unit test fixtures so I could compare headless testing to the browser.
Copy link

changeset-bot bot commented Jan 7, 2025

🦋 Changeset detected

Latest commit: 631850f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 27 packages
Name Type
@khanacademy/wonder-blocks-clickable Patch
@khanacademy/wonder-blocks-dropdown Patch
@khanacademy/wonder-blocks-core Patch
@khanacademy/wonder-blocks-accordion Patch
@khanacademy/wonder-blocks-button Patch
@khanacademy/wonder-blocks-cell Patch
@khanacademy/wonder-blocks-form Patch
@khanacademy/wonder-blocks-icon-button Patch
@khanacademy/wonder-blocks-link Patch
@khanacademy/wonder-blocks-pill Patch
@khanacademy/wonder-blocks-birthday-picker Patch
@khanacademy/wonder-blocks-banner Patch
@khanacademy/wonder-blocks-breadcrumbs Patch
@khanacademy/wonder-blocks-data Patch
@khanacademy/wonder-blocks-grid Patch
@khanacademy/wonder-blocks-icon Patch
@khanacademy/wonder-blocks-labeled-field Patch
@khanacademy/wonder-blocks-layout Patch
@khanacademy/wonder-blocks-modal Patch
@khanacademy/wonder-blocks-popover Patch
@khanacademy/wonder-blocks-progress-spinner Patch
@khanacademy/wonder-blocks-search-field Patch
@khanacademy/wonder-blocks-switch Patch
@khanacademy/wonder-blocks-testing Patch
@khanacademy/wonder-blocks-toolbar Patch
@khanacademy/wonder-blocks-tooltip Patch
@khanacademy/wonder-blocks-typography Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@khan-actions-bot khan-actions-bot requested a review from a team January 7, 2025 23:07
@khan-actions-bot
Copy link
Contributor

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to .changeset/mean-cherries-press.md, __docs__/wonder-blocks-dropdown/single-select.accessibility.stories.tsx, packages/wonder-blocks-core/src/index.ts, packages/wonder-blocks-clickable/src/components/clickable-behavior.ts, packages/wonder-blocks-core/src/util/keys.ts, packages/wonder-blocks-dropdown/src/components/option-item.tsx, packages/wonder-blocks-dropdown/src/components/select-opener.tsx, packages/wonder-blocks-dropdown/src/util/constants.ts, packages/wonder-blocks-clickable/src/components/__tests__/clickable-behavior.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/select-opener.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx

Don't want to be involved in this pull request? Comment #removeme and we won't notify you of further changes.

Copy link
Contributor

github-actions bot commented Jan 7, 2025

npm Snapshot: Published

🎉 Good news!! We've packaged up the latest commit from this PR (63b2d0f) and published all packages with changesets to npm.

You can install the packages in webapp by running:

./services/static/dev/tools/deploy_wonder_blocks.js --tag="PR2420"

Packages can also be installed manually by running:

yarn add @khanacademy/wonder-blocks-<package-name>@PR2420

Copy link
Contributor

github-actions bot commented Jan 7, 2025

A new build was pushed to Chromatic! 🚀

https://5e1bf4b385e3fb0020b7073c-qfjwerghlt.chromatic.com/

Chromatic results:

Metric Total
Captured snapshots undefined
Tests with visual changes undefined
Total stories undefined
Inherited (not captured) snapshots [TurboSnap] undefined
Tests on the build undefined

Copy link
Contributor

github-actions bot commented Jan 7, 2025

Size Change: +96 B (+0.1%)

Total Size: 96.5 kB

Filename Size Change
packages/wonder-blocks-clickable/dist/es/index.js 3.06 kB -2 B (-0.07%)
packages/wonder-blocks-core/dist/es/index.js 2.97 kB +68 B (+2.34%)
packages/wonder-blocks-dropdown/dist/es/index.js 19.1 kB +30 B (+0.16%)
ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3.77 kB
packages/wonder-blocks-banner/dist/es/index.js 1.53 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.77 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 887 B
packages/wonder-blocks-button/dist/es/index.js 4.04 kB
packages/wonder-blocks-cell/dist/es/index.js 2.01 kB
packages/wonder-blocks-data/dist/es/index.js 6.24 kB
packages/wonder-blocks-form/dist/es/index.js 6.2 kB
packages/wonder-blocks-grid/dist/es/index.js 1.36 kB
packages/wonder-blocks-icon-button/dist/es/index.js 2.95 kB
packages/wonder-blocks-icon/dist/es/index.js 871 B
packages/wonder-blocks-labeled-field/dist/es/index.js 72 B
packages/wonder-blocks-layout/dist/es/index.js 1.82 kB
packages/wonder-blocks-link/dist/es/index.js 2.28 kB
packages/wonder-blocks-modal/dist/es/index.js 5.42 kB
packages/wonder-blocks-pill/dist/es/index.js 1.65 kB
packages/wonder-blocks-popover/dist/es/index.js 4.85 kB
packages/wonder-blocks-progress-spinner/dist/es/index.js 1.52 kB
packages/wonder-blocks-search-field/dist/es/index.js 1.36 kB
packages/wonder-blocks-switch/dist/es/index.js 1.92 kB
packages/wonder-blocks-testing-core/dist/es/index.js 3.74 kB
packages/wonder-blocks-testing/dist/es/index.js 1.07 kB
packages/wonder-blocks-theming/dist/es/index.js 693 B
packages/wonder-blocks-timing/dist/es/index.js 1.8 kB
packages/wonder-blocks-tokens/dist/es/index.js 2.36 kB
packages/wonder-blocks-toolbar/dist/es/index.js 905 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.99 kB
packages/wonder-blocks-typography/dist/es/index.js 1.23 kB

compressed-size-action

Copy link
Contributor

@kevinb-khan kevinb-khan left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for figuring out why our keyboard tests were broken. I left a few suggestions in the comments (nothing blocking).

Comment on lines +564 to +565
(triggerOnEnter && keyCode === keys.enter.toLowerCase()) ||
(triggerOnSpace && keyCode === keys.space.toLowerCase())
Copy link
Contributor

Choose a reason for hiding this comment

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

If you're normalizing the key name that we're getting from the event to be lowercase, why not have the constants in keys be lowercase to begin with?

Comment on lines +559 to +560
// Allow tests to use mixed case commands ("Space" or "space")
const keyCode = e.key.toLowerCase();
Copy link
Contributor

Choose a reason for hiding this comment

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

This variable should probably be called keyName or just key to avoid confusing with e.keyCode which is a number.

Comment on lines +110 to +130
const keyCode = e.key.toLowerCase();
// Prevent default behavior for Enter key. Without this, the select
// is only open while the Enter key is pressed.
// Prevent default behavior for Space key. Without this, Safari stays in
// active state visually
if (keyCode === "Enter" || keyCode === " ") {
if (
keyCode === keys.enter.toLowerCase() ||
keyCode === keys.space.toLowerCase()
) {
this.setState({pressed: true});
e.preventDefault();
}
};

handleKeyUp: (e: React.KeyboardEvent) => void = (e) => {
const keyCode = e.key;
const keyCode = e.key.toLowerCase();
// On key up for Enter and Space, trigger the click handler
if (keyCode === "Enter" || keyCode === " ") {
if (
keyCode === keys.enter.toLowerCase() ||
keyCode.toLowerCase() === keys.space.toLowerCase()
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The variable keyCode should probably be renamed to key or keyName to avoid confusion with event.keyCode which is a number.

Comment on lines 8 to 14
export const keys = {
escape: "Escape",
tab: "Tab",
space: " ",
space: "Space",
up: "ArrowUp",
down: "ArrowDown",
} as const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can uses of this be replaced with the keys object exported by @khanacademy/wonder-blocks-core?

Copy link
Member

@beaesguerra beaesguerra left a comment

Choose a reason for hiding this comment

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

Thanks for diving into this, Marcy! Enabling the tests again can help build our confidence to new changes in our components, especially with these more complicated components 😄 I've left some comments and questions!

"@khanacademy/wonder-blocks-core": patch
---

Fixes keyboard tests in Dropdown and Clickable
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: It would be helpful to include details in the changeset around the changes to the components since it will show up in the package changelog (dropdown package example)! That way, it can also communicate changes in behaviour that consuming apps may want to know about.

For example, in ClickableBehaviour, it looks like we now check event.key instead of event.which or event.keyCode. This might impact tests in other projects so it would be helpful to come back to the changelog and see what changes could cause new behaviour!

);
};

export const UsingKeyboardSelection = {
Copy link
Member

@beaesguerra beaesguerra Jan 9, 2025

Choose a reason for hiding this comment

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

Do we want to disable this story in Chromatic tests? It seems similar to other SingleSelect stories!

Also, do we want to highlight anything around keyboard selection for accessibility? We can reference stories in the single-select.accessibility.mdx file to document anything we want to highlight!

@@ -8,7 +8,7 @@ import {ComboboxLabels} from "./types";
export const keys = {
escape: "Escape",
tab: "Tab",
space: " ",
space: "Space",
Copy link
Member

Choose a reason for hiding this comment

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

I was running into similar-ish issues recently with key events and tests in PR: #2376 (comment) !

I ended up updating checks in DropdownCore for event.keyCode/event.which to event.key as well, since event.which or event.keyCode are deprecated. Testing keyboard interactions in tests weren't triggering the correct logic since we were using deprecated fields and user-event removed support for keyCode. I'm not sure if this is the same issue with all the other tests that were disabled though!

I also updated these constants based on the keyboard event key linked in the jsdocs: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values. The expected key for space is documented to be " " so this may cause issues!

@@ -560,21 +556,21 @@ export default class ClickableBehavior extends React.Component<
if (onKeyDown) {
onKeyDown(e);
}

const keyCode = e.which || e.keyCode;
// Allow tests to use mixed case commands ("Space" or "space")
Copy link
Member

Choose a reason for hiding this comment

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

It seems interesting to modify the component logic to accommodate the casing used in tests!

It looks like user-event will also use KeyboardEvent.key (or KeyboardEvent.code). I wonder if it's enough to solve the issue in tests by using the keys constant as well so we're testing with the proper keys that the browser uses (which you've updated already!). Let me know what you think! I'm still learning more about user-event so let me know if I am missing something!

// Act
await userEvent.keyboard("{enter}");

// Assert
// // Assert
Copy link
Member

Choose a reason for hiding this comment

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

nit: extra //!

Suggested change
// // Assert
// Assert

const firstItem = await screen.findByRole("option", {
name: /item 1/,
});
expect(firstItem).toHaveFocus();
Copy link
Member

Choose a reason for hiding this comment

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

In general, it is suggested to have only one expectation in a unit test: https://khanacademy.atlassian.net/wiki/spaces/ENG/pages/98402353/Unit+Testing+Best+Practices#One-Expectation-Per-Test-Case

I am getting used this too 😅 Could we move this to another test that checks that the first option has focus?

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.

4 participants