Skip to content

Add new no-raw-button lint rule to eslint-plugin-wonder-blocks#3054

Merged
marcysutton merged 13 commits into
mainfrom
WB-2340-no-raw-button
Jun 11, 2026
Merged

Add new no-raw-button lint rule to eslint-plugin-wonder-blocks#3054
marcysutton merged 13 commits into
mainfrom
WB-2340-no-raw-button

Conversation

@marcysutton

Copy link
Copy Markdown
Member

Summary:

Adds a new lint rule to warn about raw and StyledButton elements in favor of the WB Buttoncomponent. Typically these custom implementations are lacking design system defaults and accessibility requirements like global focus styles.

Issue: WB-2340

Test plan:

  1. Ensure tests pass
  2. Review changes to ensure they are what we want:
    • Using Button in tests, rather than disabling the rule and isolating deps
    • Updating guidance in action-styles and focus-styles
    • Disallowing custom buttons in Dropdown openers
    • Updating eslint-plugin-wonder-blocks demo to extend tsconfig-common

@changeset-bot

changeset-bot Bot commented May 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 122a6e8

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

This PR includes changesets to release 20 packages
Name Type
eslint-plugin-wonder-blocks-demo Minor
@khanacademy/eslint-plugin-wonder-blocks Minor
@khanacademy/wonder-blocks-dropdown Minor
@khanacademy/wonder-blocks-icon-button Patch
@khanacademy/wonder-blocks-clickable Patch
@khanacademy/wonder-blocks-tabs Patch
@khanacademy/wonder-blocks-button Minor
@khanacademy/wonder-blocks-birthday-picker Patch
@khanacademy/wonder-blocks-banner Patch
@khanacademy/wonder-blocks-card Patch
@khanacademy/wonder-blocks-modal Patch
@khanacademy/wonder-blocks-popover Patch
@khanacademy/wonder-blocks-search-field Patch
@khanacademy/wonder-blocks-accordion Patch
@khanacademy/wonder-blocks-cell Patch
@khanacademy/wonder-blocks-form Patch
@khanacademy/wonder-blocks-link Patch
@khanacademy/wonder-blocks-pill Patch
@khanacademy/wonder-blocks-date-picker Patch
@khanacademy/wonder-blocks-tooltip 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

},
};

/**

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeeting this example out of here to de-emphasize this pattern of ClickableBehavior!

@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Size Change: +657 B (+0.51%)

Total Size: 131 kB

📦 View Changed
Filename Size Change
packages/eslint-plugin-wonder-blocks/dist/es/index.js 7.51 kB +324 B (+4.51%) 🔍
packages/wonder-blocks-button/dist/es/index.js 4.3 kB +20 B (+0.47%)
packages/wonder-blocks-dropdown/dist/es/index.js 20.1 kB +313 B (+1.59%)
ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3.02 kB
packages/wonder-blocks-announcer/dist/es/index.js 2.43 kB
packages/wonder-blocks-badge/dist/es/index.js 2.03 kB
packages/wonder-blocks-banner/dist/es/index.js 2.01 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.93 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 761 B
packages/wonder-blocks-card/dist/es/index.js 1.09 kB
packages/wonder-blocks-cell/dist/es/index.js 2.19 kB
packages/wonder-blocks-clickable/dist/es/index.js 2.61 kB
packages/wonder-blocks-core/dist/es/index.js 2.6 kB
packages/wonder-blocks-data/dist/es/index.js 5.48 kB
packages/wonder-blocks-date-picker/dist/es/index.js 8.06 kB
packages/wonder-blocks-form/dist/es/index.js 6.32 kB
packages/wonder-blocks-grid/dist/es/index.js 1.25 kB
packages/wonder-blocks-icon-button/dist/es/index.js 4.02 kB
packages/wonder-blocks-icon/dist/es/index.js 1.91 kB
packages/wonder-blocks-labeled-field/dist/es/index.js 3.47 kB
packages/wonder-blocks-layout/dist/es/index.js 1.69 kB
packages/wonder-blocks-link/dist/es/index.js 1.54 kB
packages/wonder-blocks-modal/dist/es/index.js 7.36 kB
packages/wonder-blocks-pill/dist/es/index.js 1.31 kB
packages/wonder-blocks-popover/dist/es/index.js 4.41 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 5.66 kB
packages/wonder-blocks-testing-core/dist/es/index.js 4.09 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.53 kB
packages/wonder-blocks-tokens/dist/es/index.js 5.4 kB
packages/wonder-blocks-toolbar/dist/es/index.js 921 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.06 kB
packages/wonder-blocks-typography/dist/es/index.js 1.58 kB

compressed-size-action

@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

A new build was pushed to Chromatic! 🚀

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

Chromatic results:

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

* Custom button
* </StyledButton>
* // Applying to a WB component via style prop
* <Button style={actionStyles.inverse}>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I wasn't 100% sure what to do with this one, except to avoid confusion around custom components with StyledButton. It made sense to me to swap it out for a Button component. Maybe we need to rethink this utility a bit?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wasn't sure too, I've left some questions in the comments and tagged Juan too since he may have more context around the actionStyles!

Comment thread __docs__/wonder-blocks-styles/focus-styles.stories.tsx Outdated
"skipDefaultLibCheck": true, // it's safe to assume that built-in .d.ts files are correct
"skipLibCheck": false
"moduleResolution": "node",
"noImplicitAny": true

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: The IconButton in the demo for this rule required some updates to tsconfig. There was quite a bit of overlap with the common config so I opted to change it a bit.

@marcysutton marcysutton force-pushed the WB-2340-no-raw-button branch from 9ca6b30 to 3542853 Compare May 18, 2026 23:36
@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

npm Snapshot: Published

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

You can install the packages in frontend by running:

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

Packages can also be installed manually by running:

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

@marcysutton marcysutton marked this pull request as ready for review May 18, 2026 23:40
@khan-actions-bot khan-actions-bot requested a review from a team May 18, 2026 23:40
@khan-actions-bot

Copy link
Copy Markdown
Contributor

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to pnpm-lock.yaml, .changeset/stale-taxis-grow.md, __docs__/wonder-blocks-clickable/clickable-behavior.stories.tsx, __docs__/wonder-blocks-dropdown/multi-select.accessibility.stories.tsx, __docs__/wonder-blocks-dropdown/single-select.accessibility.stories.tsx, __docs__/wonder-blocks-styles/action-styles-variants.stories.tsx, __docs__/wonder-blocks-styles/action-styles.stories.tsx, __docs__/wonder-blocks-styles/focus-styles.stories.tsx, __docs__/wonder-blocks-tabs/tabs.stories.tsx, packages/eslint-plugin-wonder-blocks/README.md, packages/wonder-blocks-announcer/package.json, packages/wonder-blocks-announcer/tsconfig-build.json, packages/wonder-blocks-card/package.json, packages/wonder-blocks-date-picker/package.json, packages/wonder-blocks-modal/package.json, packages/wonder-blocks-popover/package.json, __docs__/tools/eslint-plugin-wonder-blocks/no-raw-button.mdx, packages/eslint-plugin-wonder-blocks/demo/package.json, packages/eslint-plugin-wonder-blocks/demo/tsconfig.json, packages/eslint-plugin-wonder-blocks/docs/no-raw-button.md, packages/eslint-plugin-wonder-blocks/demo/src/no-raw-button-example.tsx, packages/eslint-plugin-wonder-blocks/src/configs/strict.ts, packages/eslint-plugin-wonder-blocks/src/rules/index.ts, packages/eslint-plugin-wonder-blocks/src/rules/no-raw-button.ts, packages/wonder-blocks-button/src/components/button-unstyled.tsx, packages/wonder-blocks-button/src/components/button.tsx, packages/wonder-blocks-button/src/util/button.types.ts, packages/wonder-blocks-clickable/src/components/clickable.tsx, packages/wonder-blocks-dropdown/src/components/select-opener.tsx, packages/wonder-blocks-icon-button/src/components/icon-button-unstyled.tsx, packages/wonder-blocks-modal/src/util/maybe-get-portal-mounted-modal-host-element.test.tsx, packages/wonder-blocks-tabs/src/components/tab.tsx, packages/eslint-plugin-wonder-blocks/src/rules/__tests__/no-raw-button.test.ts, packages/wonder-blocks-announcer/src/__tests__/components/announce-message-button.tsx, packages/wonder-blocks-button/src/components/__tests__/button.test.tsx, packages/wonder-blocks-card/src/__tests__/components/card.test.tsx, packages/wonder-blocks-clickable/src/components/__tests__/clickable-behavior.test.tsx, packages/wonder-blocks-clickable/src/components/__tests__/clickable.test.tsx, packages/wonder-blocks-core/src/util/__tests__/focus.test.tsx, packages/wonder-blocks-date-picker/src/components/__tests__/date-picker-input.test.tsx, packages/wonder-blocks-date-picker/src/components/__tests__/date-picker.test.tsx, packages/wonder-blocks-date-picker/src/components/__tests__/focus-manager.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/action-menu.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/dropdown-core.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx, packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx, packages/wonder-blocks-modal/src/components/__tests__/drawer-backdrop.test.tsx, packages/wonder-blocks-modal/src/components/__tests__/drawer-launcher.test.tsx, packages/wonder-blocks-modal/src/components/__tests__/modal-backdrop.test.tsx, packages/wonder-blocks-modal/src/components/__tests__/modal-launcher.test.tsx, packages/wonder-blocks-popover/src/components/__tests__/focus-manager.test.tsx, packages/wonder-blocks-popover/src/components/__tests__/initial-focus.test.tsx, packages/wonder-blocks-popover/src/components/__tests__/popover-anchor.test.tsx, packages/wonder-blocks-popover/src/components/__tests__/popover-content.test.tsx, packages/wonder-blocks-popover/src/components/__tests__/popover.test.tsx, packages/wonder-blocks-popover/src/util/__tests__/util.test.tsx, packages/wonder-blocks-tabs/src/components/__tests__/tab-panel.test.tsx, packages/wonder-blocks-tabs/src/components/__tests__/tablist.test.tsx, packages/wonder-blocks-tabs/src/components/__tests__/tabs.test.tsx

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

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files

Impacted file tree graph

@@     Coverage Diff      @@
##   main   #3054   +/-   ##
============================
============================

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 ad92408...3542853. 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.

@beaesguerra beaesguerra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for working on this Marcy! I started reviewing and left some questions I wanted to get your thoughts on first since they were following a similar theme! I'll start a thread on Slack too so we can discuss more!

Comment on lines +84 to +88
<IconButton
icon={magnifyingGlassIcon}
aria-label="Search"
onClick={() => {}}
/>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🎉 nice! This is the type of things we want it to catch!

Comment thread __docs__/wonder-blocks-styles/action-styles-variants.stories.tsx
* NOTE: We recommend using existing Wonder Blocks interactive components (e.g.
* `Button`, `IconButton`, `Link` etc.). These styles are meant to be used
* as a last resort when our components do not meet your needs.
* `actionStyles` is used **internally** by Wonder Blocks components to ensure

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see actionStyles is used externally in frontend! And I think the actionStyles are meant to be used externally so this might be misleading! We do prefer consumers to use WB components directly though.

cc: @jandrade in case you had thoughts on this!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's correct, actionStyles are also used in frontend to work well over dark backgrounds, so we would need to keep this while we figure a long term solution from the Design/UX perspective.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Restored!

* Custom button
* </StyledButton>
* // Applying to a WB component via style prop
* <Button style={actionStyles.inverse}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wasn't sure too, I've left some questions in the comments and tagged Juan too since he may have more context around the actionStyles!

<StyledButton style={focusStyles.focus}>
Custom button
</StyledButton>
<Button onClick={() => {}} style={focusStyles.focus}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmmm I wonder if we should also leave these as is to show that focusStyles work on elements that don't already have the focus styling!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yeah, I'd keep it this way (using the current custom element) as Button already uses focusStyles internally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reset back to the way it was!

@marcysutton marcysutton force-pushed the WB-2340-no-raw-button branch from 3542853 to 4144c10 Compare May 26, 2026 23:36
@khan-actions-bot khan-actions-bot requested a review from a team May 26, 2026 23:36
@marcysutton marcysutton force-pushed the WB-2340-no-raw-button branch 3 times, most recently from 1dcaae2 to ba7211a Compare June 4, 2026 19:28
@marcysutton marcysutton force-pushed the WB-2340-no-raw-button branch 2 times, most recently from 565967c to 9e69335 Compare June 10, 2026 17:21
@beaesguerra

Copy link
Copy Markdown
Member

@claude review once

Comment thread packages/wonder-blocks-button/src/util/button.types.ts
Comment thread __docs__/wonder-blocks-tabs/tabs.stories.tsx
Comment thread packages/eslint-plugin-wonder-blocks/docs/no-raw-button.md

@beaesguerra beaesguerra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks Marcy! Left some questions for you!

Comment thread .changeset/stale-taxis-grow.md Outdated
Comment thread packages/wonder-blocks-dropdown/src/index.ts
Comment thread __docs__/wonder-blocks-styles/focus-styles.stories.tsx
Comment thread __docs__/wonder-blocks-styles/focus-styles.stories.tsx Outdated
Comment thread __docs__/wonder-blocks-styles/focus-styles.stories.tsx Outdated
Comment thread packages/wonder-blocks-dropdown/src/components/custom-opener.tsx Outdated
aria-disabled={ariaDisabled}
onClick={disabled ? undefined : onClick}
data-testid={testId}
style={[styles.reset, disabled && styles.disabled, style]}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it worth noting that consumers will also need to provide their own disabled styles to the custom opener too? (ie. greying out the opener using semantic color disabled tokens!)

Comment thread packages/wonder-blocks-dropdown/src/components/custom-opener.tsx
Comment on lines +151 to +160

/**
* Respond to a raw "blur" event.
*/
onBlur?: (e: React.FocusEvent) => unknown;

/**
* Respond to a raw "focus" event.
*/
onFocus?: (e: React.FocusEvent) => unknown;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can add a new changeset file for a minor change in the buttons package since button components support new props!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added! I replaced the original changeset with a couple of targeted ones.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for updating the changesets! I don't see a changeset for the new button props though!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, good catch! I missed one in the swirl of changesets. Added it!

Comment on lines -376 to -381
style={[
styles.customOpener,
focused && styles.focused,
hovered && styles.hovered,
pressed && styles.pressed,
]}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be helpful for the custom opener examples to show full implementations of how the custom opener should handle all states? (Disabled, hover, pressed, error)

These are the things that the default opener comes with, which is why it is preferred over custom implementations!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, added some styles back in. I put them on a styles.root object so the child BodyText can be customized too!

@marcysutton marcysutton force-pushed the WB-2340-no-raw-button branch from 9e69335 to bca04fc Compare June 11, 2026 17:01
@khan-actions-bot khan-actions-bot requested a review from a team June 11, 2026 17:01

@beaesguerra beaesguerra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like the CustomOpener story is rendering on 2 lines, is that expected? Might also be related to the new shift in Chromatic snapshots!

Image

Only noticed a few more minor things:

  • the missing changeset for the new button props
  • enabling snapshots for all themes

After that, should be good to go!

tags: ["!autodocs", "!manifest"],
parameters: {
chromatic: {
modes: themeModes,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you switch this to allThemeModes? That will include the new dark theme! (I will be removing themeModes soon after landing the dark mode changes!)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, updated!

…seButtonProps and a related ActivityButton fix
@khan-actions-bot khan-actions-bot requested a review from a team June 11, 2026 21:54

@beaesguerra beaesguerra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me! 🚀

@marcysutton marcysutton merged commit d69335d into main Jun 11, 2026
15 checks passed
@marcysutton marcysutton deleted the WB-2340-no-raw-button branch June 11, 2026 22:40
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