Skip to content

Refactor: Extract shared PseudoStateGrid component in pseudo-states stories#34334

Merged
valentinpalkovic merged 3 commits into
nextfrom
copilot/fix-duplicate-pseudo-state-grid
Mar 26, 2026
Merged

Refactor: Extract shared PseudoStateGrid component in pseudo-states stories#34334
valentinpalkovic merged 3 commits into
nextfrom
copilot/fix-duplicate-pseudo-state-grid

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 26, 2026

The 8-cell pseudo-state combination grid (Normal, Hover, Focus, Active, and their combinations) was copy-pasted verbatim across 6 story files in addon-pseudo-states, totalling ~200 lines of near-identical JSX and creating a maintenance hazard.

Changes

  • New PseudoStateGrid.tsx — shared component accepting a render: (label: string) => ReactNode prop that owns the grid layout and all pseudo-state class combinations
  • Refactored All story in all 6 files to delegate to PseudoStateGrid:
    • Button.stories.tsx
    • CSSAtRules.stories.tsx
    • CustomElement.stories.tsx
    • CustomElementNested.stories.tsx
    • ShadowRoot.stories.tsx
    • ShadowRootWithPart.stories.tsx

Usage

export const All: Story = {
  render: (args) => (
    <PseudoStateGrid render={(label) => <Button {...args}>{label}</Button>} />
  ),
};

Adding a new pseudo-state or changing the grid layout now requires a single edit. Existing DynamicStyles stories that call All.render! directly continue to work unchanged.

Original prompt

This section details on the original issue you should resolve

<issue_title>Duplicate Code: Repeated pseudo-state grid render in 6 story files</issue_title>
<issue_description>Analysis of commit 6798300

Assignee: @copilot

Summary

The All story export with an 8-cell pseudo-state grid (Normal, Hover, Focus, Active, Hover+Focus, Hover+Active, Focus+Active, Hover+Focus+Active) is copy-pasted nearly verbatim across 6 story files in the addon-pseudo-states package. Each instance is 28–40 lines of near-identical JSX, differing only in the child component used.

Duplication Details

Pattern: Pseudo-state combination grid in All story export

  • Severity: Medium

  • Occurrences: 6 instances

  • Locations:

    • code/addons/pseudo-states/src/stories/Button.stories.tsx (lines 19–49)
    • code/addons/pseudo-states/src/stories/CSSAtRules.stories.tsx (lines 22–52)
    • code/addons/pseudo-states/src/stories/CustomElement.stories.tsx (lines 21–59)
    • code/addons/pseudo-states/src/stories/CustomElementNested.stories.tsx (lines 21–59)
    • code/addons/pseudo-states/src/stories/ShadowRoot.stories.tsx (lines 17–47)
    • code/addons/pseudo-states/src/stories/ShadowRootWithPart.stories.tsx (lines 17–47)
  • Code Sample (representative, from Button.stories.tsx):

    export const All: Story = {
      render: (args) => (
        (div className="story-grid")
          (div)(Button {...args})Normal(/Button)(/div)
          (div className="pseudo-hover-all")(Button {...args})Hover(/Button)(/div)
          (div className="pseudo-focus-all")(Button {...args})Focus(/Button)(/div)
          (div className="pseudo-active-all")(Button {...args})Active(/Button)(/div)
          (div className="pseudo-hover-all pseudo-focus-all")(Button {...args})Hover Focus(/Button)(/div)
          (div className="pseudo-hover-all pseudo-active-all")(Button {...args})Hover Active(/Button)(/div)
          (div className="pseudo-focus-all pseudo-active-all")(Button {...args})Focus Active(/Button)(/div)
          (div className="pseudo-hover-all pseudo-focus-all pseudo-active-all")(Button {...args})Hover Focus Active(/Button)(/div)
        (/div)
      ),
    };

Additionally, the Default, Hover, Focus, and Active story exports with identical parameters: { pseudo: { (state): true } } shapes are repeated in all 6 files (~8–12 lines each).

Impact Analysis

  • Maintainability: Adding a new pseudo-state (e.g., focus-within) requires updating all 6 files individually. Any UI layout change to the grid must be replicated 6 times.
  • Bug Risk: Inconsistencies are already present (e.g., Button.stories.tsx uses args in the render, CustomElement doesn't). A fix in one file is unlikely to be applied to the others.
  • Code Bloat: ~200 lines of redundant JSX across the stories directory.

Refactoring Recommendations

  1. Extract a shared PseudoStateGrid component

    • Create: code/addons/pseudo-states/src/stories/PseudoStateGrid.tsx
    • Accept a render: (label: string) => ReactNode prop for the child component
    • Replace all All renders with (PseudoStateGrid render={(label) =) (Button {...args}){label}(/Button)} />
    • Estimated effort: 1–2 hours
    • Benefits: Single source of truth for grid layout, easier to extend with new states
  2. Extract shared base story exports

    • Define Default, Hover, Focus, Active once in a shared file and re-export or spread into each story file
    • Estimated effort: 30 minutes

Implementation Checklist

  • Review duplication findings
  • Create PseudoStateGrid shared component
  • Refactor all 6 story files to use the shared component
  • Extract shared base story exports
  • Verify no visual/functional regression in Storybook

Analysis Metadata

  • Analyzed Files: 6
  • Detection Method: Serena semantic code analysis + manual pattern comparison
  • Commit: 6798300
  • Analysis Date: 2026-03-23

Generated by Duplicate Code Detector ·

To install this agentic workflow, run

gh aw add github/gh-aw/.github/workflows/duplicate-code-detector.md@852cb06ad52958b402ed982b69957ffc57ca0619

Comments on the Issue (you are @Copi...


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

…cate code in pseudo-states stories

Co-authored-by: valentinpalkovic <5889929+valentinpalkovic@users.noreply.github.com>
Agent-Logs-Url: https://github.com/storybookjs/storybook/sessions/d64da8e9-5f2a-402f-92c2-bc4185d77af0
Copilot AI changed the title [WIP] Fix duplicate code in pseudo-state grid across story files Refactor: Extract shared PseudoStateGrid component in pseudo-states stories Mar 26, 2026
Copilot AI requested a review from valentinpalkovic March 26, 2026 09:59
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Mar 26, 2026

View your CI Pipeline Execution ↗ for commit a413a7f

Command Status Duration Result
nx run-many -t compile,check,knip,test,lint,fmt... ✅ Succeeded 1m 16s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-26 14:32:54 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 26, 2026

Fails
🚫 PR description is missing the mandatory "#### Manual testing" section. Please add it so that reviewers know how to manually test your changes.

Generated by 🚫 dangerJS against a413a7f

@valentinpalkovic valentinpalkovic marked this pull request as ready for review March 26, 2026 14:29
@valentinpalkovic valentinpalkovic added the maintenance User-facing maintenance tasks label Mar 26, 2026
@valentinpalkovic valentinpalkovic merged commit 810093c into next Mar 26, 2026
125 of 128 checks passed
@valentinpalkovic valentinpalkovic deleted the copilot/fix-duplicate-pseudo-state-grid branch March 26, 2026 14:30
@github-actions github-actions Bot mentioned this pull request Mar 26, 2026
10 tasks
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 26, 2026

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 738718f4-edfb-4037-a309-b85d123a51b5

📥 Commits

Reviewing files that changed from the base of the PR and between d19270e and a413a7f.

📒 Files selected for processing (7)
  • code/addons/pseudo-states/src/stories/Button.stories.tsx
  • code/addons/pseudo-states/src/stories/CSSAtRules.stories.tsx
  • code/addons/pseudo-states/src/stories/CustomElement.stories.tsx
  • code/addons/pseudo-states/src/stories/CustomElementNested.stories.tsx
  • code/addons/pseudo-states/src/stories/PseudoStateGrid.tsx
  • code/addons/pseudo-states/src/stories/ShadowRoot.stories.tsx
  • code/addons/pseudo-states/src/stories/ShadowRootWithPart.stories.tsx

📝 Walkthrough

Walkthrough

A new reusable PseudoStateGrid component was created to centralize the rendering of pseudo-state grid layouts. Six story files were refactored to use this component instead of maintaining individual hardcoded grid structures with duplicate label and CSS class logic.

Changes

Cohort / File(s) Summary
New Component
code/addons/pseudo-states/src/stories/PseudoStateGrid.tsx
Introduced PseudoStateGrid component that accepts a render callback to generate pseudo-state variations (Normal, Hover, Focus, Active, and combinations). Handles grid layout and CSS class application for all pseudo-state combinations.
Refactored Story Files
code/addons/pseudo-states/src/stories/Button.stories.tsx, CSSAtRules.stories.tsx, CustomElement.stories.tsx, CustomElementNested.stories.tsx, ShadowRoot.stories.tsx, ShadowRootWithPart.stories.tsx
Replaced hardcoded grid layouts with PseudoStateGrid component. Each story now passes a render callback to delegate label generation and pseudo-state rendering, removing duplicate DOM structure and CSS class management.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~20 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


Comment @coderabbitai help to get the list of available commands and usage tips.

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

Labels

ci:normal maintenance User-facing maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate Code: Repeated pseudo-state grid render in 6 story files

2 participants