Skip to content

chore(EmptyButton): migrate to CSS Modules with visual regression baseline#1105

Open
DreaminDani wants to merge 4 commits into
mainfrom
chore/migrate-emptybutton-css-modules
Open

chore(EmptyButton): migrate to CSS Modules with visual regression baseline#1105
DreaminDani wants to merge 4 commits into
mainfrom
chore/migrate-emptybutton-css-modules

Conversation

@DreaminDani

Copy link
Copy Markdown
Contributor

Commits

  • test(EmptyButton): add visual regression baseline before CSS Modules migration — adds a story, a Playwright spec, and fresh PNG snapshots capturing the current styled-components rendering.
  • chore(EmptyButton): migrate styling from styled-components to CSS Modules — replaces styled.button with a forwardRef <button> component + .module.css. DOM-identical, byte-for-byte visual parity verified.

Cluster-aware approach

EmptyButton is extended via styled(EmptyButton) by CrossButton, the Popover close button, and the CodeBlock copy button, and is rendered directly by Collapsible. Following the Container precedent (#1101), the base rule is scoped with :where(.empty-button) so it sits at zero specificity — equal-specificity consumer overrides (background/padding/color) win by source order without an && hack. Those consumers keep their existing styled(EmptyButton) / <EmptyButton> usage unchanged and will migrate under their own follow-up tickets.

Verification

  • yarn test:visual tests/buttons/emptybutton.spec.ts — passes with zero snapshot regenerations
  • Consumer parity confirmed against existing baselines with zero regenerations: tests/display/dialog.spec.ts (renders CrossButton), tests/display/sidebar-collapsible-item.spec.ts and tests/display/sidebar-collapsible-title.spec.ts (render <EmptyButton> via Collapsible)
  • Confirmed at runtime that CodeBlock's $copied/$error transient props do not leak to the DOM and produce no React "unknown DOM attribute" warnings
  • yarn lint:css, yarn lint:code, yarn format, yarn build all green
  • No styled-components import remains in src/components/EmptyButton/

DreaminDani and others added 2 commits June 25, 2026 12:33
…migration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ules

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DreaminDani DreaminDani self-assigned this Jun 25, 2026
@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 90092ad

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

This PR includes changesets to release 1 package
Name Type
@clickhouse/click-ui 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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 443f9a6. Configure here.

Comment thread src/components/EmptyButton/EmptyButton.tsx

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates EmptyButton from styled-components to CSS Modules while adding Storybook + Playwright visual-regression coverage to lock in rendering parity across the migration. It follows the same “zero-specificity base rule” approach previously used for Container to keep downstream styled(EmptyButton) overrides working without && specificity hacks.

Changes:

  • Added Storybook stories and a new Playwright visual-regression spec for EmptyButton (including light/dark and interactive states).
  • Replaced styled.button implementation with a forwardRef <button> component wired to EmptyButton.module.css.
  • Added a changeset entry for a patch release documenting the migration.

Reviewed changes

Copilot reviewed 5 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/buttons/emptybutton.spec.ts Adds visual regression coverage and basic interaction checks for EmptyButton.
src/components/EmptyButton/EmptyButton.tsx Replaces styled-components implementation with a forwardRef button using CSS Modules + cn.
src/components/EmptyButton/EmptyButton.stories.tsx Adds Storybook stories and a harness decorator for consistent snapshots.
src/components/EmptyButton/EmptyButton.module.css Introduces CSS Module styles using :where() to keep the base selector at zero specificity.
.changeset/migrate-emptybutton-to-css-modules.md Records the migration as a patch-level change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/EmptyButton/EmptyButton.module.css Outdated
DreaminDani and others added 2 commits June 25, 2026 15:40
… win

The .empty-button:disabled rule sat at specificity (0,2,0), tying with a
downstream styled(EmptyButton) &:disabled (also 0,2,0) and winning by CSS
Module source order — the exact failure :where() prevents on the base rule.
Scope it as :where(.empty-button):disabled (0,1,0) so consumer :disabled
overrides win naturally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Popover's close button (CloseButton as={RadixPopover.Close}) and CodeBlock's
copy/wrap buttons (CodeButton as={IconButton}) render styled(EmptyButton) via
the styled-components `as` prop, which bypasses EmptyButton's own render — so
EmptyButton's module class never reaches the DOM. There were no visual specs
covering these paths, so the migration was unverified there.

Add open-by-default Popover and a CodeBlock-with-wrap-button story plus
Playwright specs (light + dark, copied state). Baselines were generated against
the pre-migration styled.button EmptyButton and pass byte-for-byte against the
migrated CSS Modules version: the lost base reset is inconsequential because
each `as` target supplies its own styling (IconButton's own resets; the cross
Icon is an svg that never inherited button resets). These specs lock that in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@workflow-authentication-public

Copy link
Copy Markdown
Contributor

Storybook Preview Deployed

✅ Preview URL: https://click-owx63ansm-clickhouse.vercel.app

Built from commit: 81bf81d8eaa2e0fcec3442cba80d9f47494a8e73

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.

2 participants