Skip to content

Conversation

@marcysutton
Copy link
Member

Summary:

There was an issue with ModalLauncher not returning focus when called from inside of a table. It was hard to reproduce, so it took a while to find the problem. I finally narrowed it down to the scheduler not quite working properly for this scenario. I added a story reproduction that will show it working before the change and fixed after.

A real page where this can be reproduced: https://classroom.khanacademy.dev/teacher/class/HQV5EE72/reports/assignments

Issue: WB-2103

Test plan:

  1. Visit the new story: /?path=/story/packages-modal-modallauncher--focus-management-pattern
  2. Ensure focus returns to the correct launcher button on close

@changeset-bot
Copy link

changeset-bot bot commented Oct 21, 2025

🦋 Changeset detected

Latest commit: 7e5c73d

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

This PR includes changesets to release 5 packages
Name Type
@khanacademy/wonder-blocks-modal Patch
@khanacademy/wonder-blocks-dropdown Patch
@khanacademy/wonder-blocks-popover Patch
@khanacademy/wonder-blocks-tooltip Patch
@khanacademy/wonder-blocks-birthday-picker 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 October 21, 2025 22:34
@khan-actions-bot
Copy link
Contributor

khan-actions-bot commented Oct 21, 2025

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to .changeset/kind-melons-guess.md, __docs__/wonder-blocks-modal/modal-launcher.stories.tsx, packages/wonder-blocks-modal/src/components/modal-launcher.tsx, packages/wonder-blocks-modal/src/components/__tests__/modal-launcher.test.tsx

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

@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

Size Change: +50 B (+0.05%)

Total Size: 109 kB

Filename Size Change
packages/wonder-blocks-modal/dist/es/index.js 7.05 kB +50 B (+0.71%)
ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3 kB
packages/wonder-blocks-announcer/dist/es/index.js 1.74 kB
packages/wonder-blocks-badge/dist/es/index.js 1.91 kB
packages/wonder-blocks-banner/dist/es/index.js 2.01 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.92 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 755 B
packages/wonder-blocks-button/dist/es/index.js 4.25 kB
packages/wonder-blocks-card/dist/es/index.js 1.01 kB
packages/wonder-blocks-cell/dist/es/index.js 2.19 kB
packages/wonder-blocks-clickable/dist/es/index.js 2.66 kB
packages/wonder-blocks-core/dist/es/index.js 2.48 kB
packages/wonder-blocks-data/dist/es/index.js 5.48 kB
packages/wonder-blocks-dropdown/dist/es/index.js 19.4 kB
packages/wonder-blocks-form/dist/es/index.js 6.2 kB
packages/wonder-blocks-grid/dist/es/index.js 1.24 kB
packages/wonder-blocks-icon-button/dist/es/index.js 3.16 kB
packages/wonder-blocks-icon/dist/es/index.js 1.91 kB
packages/wonder-blocks-labeled-field/dist/es/index.js 3.48 kB
packages/wonder-blocks-layout/dist/es/index.js 1.63 kB
packages/wonder-blocks-link/dist/es/index.js 1.52 kB
packages/wonder-blocks-pill/dist/es/index.js 1.31 kB
packages/wonder-blocks-popover/dist/es/index.js 4.3 kB
packages/wonder-blocks-progress-spinner/dist/es/index.js 1.48 kB
packages/wonder-blocks-search-field/dist/es/index.js 1.1 kB
packages/wonder-blocks-styles/dist/es/index.js 464 B
packages/wonder-blocks-switch/dist/es/index.js 1.55 kB
packages/wonder-blocks-tabs/dist/es/index.js 3.71 kB
packages/wonder-blocks-testing-core/dist/es/index.js 3.25 kB
packages/wonder-blocks-testing/dist/es/index.js 978 B
packages/wonder-blocks-theming/dist/es/index.js 384 B
packages/wonder-blocks-timing/dist/es/index.js 1.37 kB
packages/wonder-blocks-tokens/dist/es/index.js 4.99 kB
packages/wonder-blocks-toolbar/dist/es/index.js 906 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.4 kB
packages/wonder-blocks-typography/dist/es/index.js 1.57 kB

compressed-size-action

@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

npm Snapshot: Published

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

You can install the packages in frontend by running:

./dev/tools/deploy_wonder_blocks.js --tag="PR2825"

Packages can also be installed manually by running:

pnpm add @khanacademy/wonder-blocks-<package-name>@PR2825

@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

A new build was pushed to Chromatic! 🚀

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

Chromatic results:

Metric Total
Captured snapshots 356
Tests with visual changes 0
Total stories 730
Inherited (not captured) snapshots [TurboSnap] 260
Tests on the build 438

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.

Tested out the functionality using the test plan, it looks good to me!

},
};

export const WithOpenedTrue = () => {
Copy link
Member

Choose a reason for hiding this comment

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

It would be helpful to include the purpose of the story in the description, or we could make the story name more specific!

Copy link
Member Author

Choose a reason for hiding this comment

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

I added a description! I think the OpenedTrue part is helpful to see when you're working with these modals.

// Use the scheduler to ensure testability
schedule.animationFrame(() => focusElement.focus(), {
schedulePolicy: SchedulePolicy.Immediately,
clearPolicy: ClearPolicy.Resolve,
Copy link
Member

Choose a reason for hiding this comment

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

Interesting, I haven't worked with our timing utilities yet, so this is good to know!

Looking into it, the default schedulePolicy for animationFrame is ClearPolicy.Cancel. Does that mean setting it to ClearPolicy.Resolve is what addresses the bug?

Copy link
Member Author

Choose a reason for hiding this comment

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

Apparently so! The clearPolicy change is the critical line to making the scheduler fire for some reason. Without it, the code fails silently. The schedulePolicy doesn't seem to have an effect.

@codecov
Copy link

codecov bot commented Oct 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (f62979c) to head (4d4719c).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##   dialog-upgrades   #2825   +/-   ##
=======================================
=======================================

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f62979c...4d4719c. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Base automatically changed from dialog-upgrades to main October 23, 2025 21:35
It specifically tests the case where a modal starts with opened=true and
needs to return focus to the last focused element when closed.
*/
export const ControlledModalFocusTest: StoryComponentType = () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

This one is failing to work now (I thought I remembered it working, but maybe not?). I'll play with it, but nothing is logged at all when the state to control the dialog is set to false....I suspect it's because the ModalLauncher isn't rendered at all after it gets closed, so there is no logging or focus anymore at that point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants