Skip to content

feat(banner): add x for closing (with prehydration)#9009

Closed
trivikr wants to merge 5 commits into
nodejs:mainfrom
trivikr:banner-close-with-pre-hydration
Closed

feat(banner): add x for closing (with prehydration)#9009
trivikr wants to merge 5 commits into
nodejs:mainfrom
trivikr:banner-close-with-pre-hydration

Conversation

@trivikr

@trivikr trivikr commented Jul 15, 2026

Copy link
Copy Markdown
Member

Reference PR with prehydration which removes flash from #9008 (comment)

This is not meant to be merged. It should be used as a reference by active maintainers to examine commit dc9e701

Copilot AI review requested due to automatic review settings July 15, 2026 07:00
@trivikr trivikr requested a review from a team as a code owner July 15, 2026 07:00
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodejs-org Ready Ready Preview Jul 15, 2026 7:10am

Request Review

@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
UI-only banner dismissal and localStorage; no auth, data, or payment paths touched.

Overview
Adds dismissible website banners with a close control and persistence in localStorage (keyed by banner text via BANNER_DISMISSAL_STORAGE_KEY).

The shared Banner component gains an optional onClose handler and a positioned × button; WithBanner is now a client component that reads/writes dismissal state and only renders when the banner is in its date window and not dismissed.

To avoid a flash of a dismissed banner before React hydrates, the root layout injects a small inline pre-hydration script in <head> that sets html[data-banner-dismissed='true'] when stored dismissal matches the current index banner; site CSS hides elements marked data-dismissible-banner under that attribute. @node-core/ui-components is bumped to 1.7.2 with a Storybook Dismissible story.

Reviewed by Cursor Bugbot for commit f2f736b. Bugbot is set up for automated code reviews on this repo. Configure here.

@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, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dc9e701. Configure here.

localStorage.getItem(BANNER_DISMISSAL_STORAGE_KEY) === banner.text
);
}
}, [banner]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dismissed banner flashes on remount

Medium Severity

After closing the banner in the same visit, client-side navigation that swaps page layouts remounts WithNavBar with dismissed reset to false. Dismissal is reapplied only in a useEffect after paint, and onClose does not set html[data-banner-dismissed], so the pre-hydration CSS hide does not apply and the banner can flash until the effect runs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit dc9e701. Configure here.

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

Adds a dismiss (“×”) control to the shared Banner UI component and wires the Nodejs.org site banner to persist dismissal via localStorage, including a pre-hydration inline script + CSS to avoid a dismissed-banner flash on first paint.

Changes:

  • Add optional dismiss button support to @node-core/ui-components Banner (via onClose) and update styling/stories.
  • Add site-level dismissal persistence (localStorage) and pre-hydration hiding to prevent first-paint flash for dismissed banners.
  • Introduce a shared storage key constant and global CSS rule to hide the banner when marked dismissed.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/ui-components/src/Common/Banner/index.tsx Adds optional close button support to the shared Banner component.
packages/ui-components/src/Common/Banner/index.module.css Makes banner container relative and positions the close button.
packages/ui-components/src/Common/Banner/index.stories.tsx Adds a Storybook story for a dismissible banner.
packages/ui-components/package.json Bumps @node-core/ui-components version.
apps/site/util/banner.ts Introduces a shared localStorage key constant for banner dismissal.
apps/site/styles/index.css Adds a global CSS rule to hide dismissible banners when the root is marked dismissed.
apps/site/components/withBanner.tsx Implements dismiss logic + persistence and passes onClose to Banner.
apps/site/app/[locale]/layout.tsx Injects a pre-hydration inline script to set a root data attribute before first paint.

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

Comment on lines 12 to 15
export type BannerProps = {
type?: 'default' | 'warning' | 'error';
onClose?: MouseEventHandler<HTMLButtonElement>;
} & HTMLAttributes<HTMLElement>;
Comment on lines +23 to +27
useEffect(() => {
if (banner) {
setDismissed(
localStorage.getItem(BANNER_DISMISSAL_STORAGE_KEY) === banner.text
);
Comment on lines +34 to +37
const onClose = () => {
localStorage.setItem(BANNER_DISMISSAL_STORAGE_KEY, banner.text);
setDismissed(true);
};
@github-actions

Copy link
Copy Markdown
Contributor

📦 Build Size Comparison

Summary

Metric Value
Old Total First Load JS 10.70 MB
New Total First Load JS 10.64 MB
Delta -59.74 KB (-0.55%)

Changes

🔄 Modified Routes (4)
Route Old First Load JS New First Load JS Delta
/[locale] 2.55 MB 2.54 MB 📉 -14.93 KB (-0.57%)
/[locale]/[...path] 2.55 MB 2.54 MB 📉 -14.93 KB (-0.57%)
/[locale]/blog/[...path] 2.55 MB 2.54 MB 📉 -14.93 KB (-0.57%)
/[locale]/download/archive/[version] 2.55 MB 2.54 MB 📉 -14.93 KB (-0.57%)

@trivikr

trivikr commented Jul 15, 2026

Copy link
Copy Markdown
Member Author

Removed dangerouslySetInnerHTML in f2f736b (this PR)

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.57%. Comparing base (61cd932) to head (f2f736b).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9009      +/-   ##
==========================================
- Coverage   74.58%   74.57%   -0.02%     
==========================================
  Files         100      100              
  Lines        8801     8801              
  Branches      324      324              
==========================================
- Hits         6564     6563       -1     
- Misses       2233     2234       +1     
  Partials        4        4              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@avivkeller avivkeller 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.

Explicitly blocking


const banner = siteConfig.websiteBanners.index;

const bannerPreHydrationScript = `try{var s=document.currentScript;if(s&&localStorage.getItem(s.dataset.storageKey)===s.dataset.banner){document.documentElement.dataset.bannerDismissed='true'}}catch{}`;

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.

Yea... that's not happening

@trivikr trivikr closed this Jul 15, 2026
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.

3 participants