Skip to content

fix: replace window.closeLangSwitcher global with CustomEvent (#1456)#1457

Open
xyaz1313 wants to merge 1 commit intokubestellar:mainfrom
xyaz1313:fix/global-window-pollution-1456
Open

fix: replace window.closeLangSwitcher global with CustomEvent (#1456)#1457
xyaz1313 wants to merge 1 commit intokubestellar:mainfrom
xyaz1313:fix/global-window-pollution-1456

Conversation

@xyaz1313
Copy link
Copy Markdown

Fixes #1456

Problem

The component uses to coordinate closing the language switcher when other dropdowns are hovered or Escape is pressed. This:

  • Pollutes the global namespace
  • Is fragile (name collisions with other scripts)
  • Has no cleanup when the component unmounts
  • Requires eslint-disable comments for @typescript-eslint/no-explicit-any

Solution

Replaced the global function with a CustomEvent pattern:

Dispatchers (2 locations)

  • Dropdown hover (line 64): window.dispatchEvent(new CustomEvent('close-lang-switcher'))
  • Escape key (line 137): same dispatch

Listener (LanguageSwitcher section)

  • window.addEventListener('close-lang-switcher', handleCloseLangSwitcher)
  • Handler stored in a ref for proper cleanup

Cleanup

  • useEffect return function removes the event listener on unmount
  • Also cleans up the setTimeout timer

Changes

  • 1 file changed: src/components/Navbar.tsx
  • 16 insertions(+), 14 deletions(-)
  • Removed all eslint-disable comments related to window.closeLangSwitcher
  • Zero functional changes — behavior is identical

Testing

  • Verify hovering Contribute/Community/GitHub dropdowns closes the language switcher
  • Verify pressing Escape closes the language switcher
  • Verify navigating away and back doesn't leak event listeners

Fixes kubestellar#1456

The Navbar component was using (window as any).closeLangSwitcher for
cross-component communication, which:
- Pollutes the global namespace
- Is fragile (name collisions)
- Has no cleanup on unmount
- Requires eslint-disable comments for any-casts

Replaced with a 'close-lang-switcher' CustomEvent:
- Dropdown hover: window.dispatchEvent(new CustomEvent('close-lang-switcher'))
- Escape key: same dispatch pattern
- LanguageSwitcher section: window.addEventListener('close-lang-switcher', ...)
- Proper cleanup in useEffect return function
Copilot AI review requested due to automatic review settings April 14, 2026 19:00
@kubestellar-prow kubestellar-prow bot added the dco-signoff: no Indicates the PR's author has not signed the DCO. label Apr 14, 2026
@kubestellar-prow
Copy link
Copy Markdown

Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

📝 Please follow instructions in the contributing guide to update your commits with the DCO

Full details of the Developer Certificate of Origin can be found at developercertificate.org.

The list of commits missing DCO signoff:

  • 9342ddf fix: replace window.closeLangSwitcher global with CustomEvent
Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@kubestellar-prow
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 14, 2026

Deploy Preview for kubestellar-docs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 9342ddf
🔍 Latest deploy log https://app.netlify.com/projects/kubestellar-docs/deploys/69de8ed8decb4a00089785e7
😎 Deploy Preview https://deploy-preview-1457--kubestellar-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@kubestellar-prow kubestellar-prow bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 14, 2026
@kubestellar-prow
Copy link
Copy Markdown

Hi @xyaz1313. Thanks for your PR.

I'm waiting for a kubestellar member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@github-actions
Copy link
Copy Markdown
Contributor

👋 Welcome to the KubeStellar community! 💖

Thanks and congrats 🎉 for opening your first PR here! We're excited to have you contributing.

Before merge, please ensure:

  • DCO Sign-off — All commits signed with git commit -s (DCO)
  • PR Title — Starts with an emoji: ✨ feature | 🐛 bug fix | 📖 docs | 🌱 infra/tests | ⚠️ breaking

📬 If you're using KubeStellar in your organization, please add your name to our Adopters list. 🙏 It really helps the project gain momentum and credibility — a small contribution back with a big impact.

Resources:

A maintainer will review your PR soon. Hope you have a great time here!

🌟 ~~~~~~~~~~ 🌟

📬 If you like KubeStellar, please ⭐ star ⭐ our repo to support it!

🙏 It really helps the project gain momentum and credibility — a small contribution back with a big impact.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 updates the Navbar component’s cross-dropdown coordination by removing the window.closeLangSwitcher global and replacing it with a CustomEvent-based pattern, reducing global namespace pollution and enabling proper listener cleanup.

Changes:

  • Replaced calls to window.closeLangSwitcher() with window.dispatchEvent(new CustomEvent("close-lang-switcher")) from hover and Escape key paths.
  • Added a window.addEventListener("close-lang-switcher", ...) listener in the LanguageSwitcher initialization flow.
  • Added a useEffect cleanup to clear the LanguageSwitcher init timer and unregister the window event listener.

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

Comment thread src/components/Navbar.tsx
Comment on lines +64 to 65
window.dispatchEvent(new CustomEvent("close-lang-switcher"));

Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The "close-lang-switcher" event name is duplicated in multiple places (two dispatch sites and the listener). To avoid future typos/mismatches, consider extracting it into a single constant (e.g. const CLOSE_LANG_SWITCHER_EVENT = ...) and reusing it for dispatch + add/removeEventListener.

Copilot uses AI. Check for mistakes.
Comment thread src/components/Navbar.tsx
Comment on lines +369 to +375
return () => {
clearTimeout(langTimer);
if (langSwitcherCloseHandlerRef.current) {
window.removeEventListener("close-lang-switcher", langSwitcherCloseHandlerRef.current);
langSwitcherCloseHandlerRef.current = null;
}
};
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The new useEffect cleanup only clears the LanguageSwitcher init timer and removes the window "close-lang-switcher" listener, but this effect also registers several other long-lived listeners/observers (e.g. document.addEventListener('keydown', ...), multiple container.addEventListener(...) handlers in initDropdowns, and a MutationObserver in initLanguageSwitcher). If Navbar ever unmounts/remounts, these will accumulate and can cause duplicated Escape handling + memory leaks. Consider capturing those handlers/observer instances and removing/disconnecting them in the same cleanup (or adjust the PR description/tests if full cleanup is out of scope).

Copilot uses AI. Check for mistakes.
@clubanderson
Copy link
Copy Markdown
Contributor

Hi @xyaz1313 — gentle reminder that this PR is blocked on DCO. The dco check is failing because one or more commits lack a Signed-off-by trailer. To fix:

git commit --amend -s       # if single commit
# OR
git rebase --signoff master && git push --force-with-lease  # for multiple commits

Once DCO passes, I can bypass tide and merge this. Thanks!

Copy link
Copy Markdown
Contributor

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

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

LGTM. Clean refactor replacing the window.closeLangSwitcher global-pollution pattern with a proper CustomEvent bus. The changes:

  1. Navbar dispatches close-lang-switcher CustomEvent instead of calling (window as any).closeLangSwitcher().
  2. Language switcher listens for the event via addEventListener.
  3. Listener is properly removed in effect cleanup (via langSwitcherCloseHandlerRef).
  4. setTimeout id is captured and cleared in cleanup.

Two small pre-existing any ESLint disables got removed as a nice side benefit.

Thanks @xyaz1313. Note to maintainers: external contributor — awaiting human approval to merge.

@kubestellar-prow kubestellar-prow bot added the lgtm Indicates that a PR is ready to be merged. label Apr 15, 2026
@kubestellar-prow
Copy link
Copy Markdown

LGTM label has been added.

DetailsGit tree hash: dc36feb2439e8ffe98c73867cbc8d5a12e34106c

@clubanderson
Copy link
Copy Markdown
Contributor

Thanks @xyaz1313 — the PR is approved and the fix looks correct, but it's blocked on two CI checks:

  1. DCO fail — commits are missing the Signed-off-by trailer. To fix:

    git commit --amend --no-edit -s
    git push -f origin fix/global-window-pollution-1456

    (Or if you have multiple commits: git rebase HEAD~N --signoff && git push -f)

  2. apply-copilot-review fail — likely downstream of the DCO issue; should clear once DCO passes.

Once DCO is green we can land this.

@clubanderson
Copy link
Copy Markdown
Contributor

@xyaz1313 thanks for the fix — this has lgtm but is blocked on DCO. The commit needs a Signed-off-by: trailer. You can fix it by running:

git commit --amend -s --no-edit
git push --force-with-lease origin main

Let us know if you need help — once signed, this can merge.

@clubanderson
Copy link
Copy Markdown
Contributor

Thanks for the fix! Two items to address before this can merge:

  1. DCO check is failing — the commit is missing a Signed-off-by: trailer. Please amend the commit with the DCO signoff:

    git commit --amend -s --no-edit
    git push --force-with-lease origin fix/global-window-pollution-1456
    

    (-s adds Signed-off-by: Your Name <your.email@example.com> to the commit message.)

  2. Merge conflict with main — PR 🐛 Add cleanup logic to prevent event listener memory leaks in navbar #1470 has since landed and refactored the same useEffect cleanup block. You'll need to rebase onto main after signing off. The cleanups.push(...) array pattern now exists in main; the event-listener removal should use that same pattern (no need for the separate langSwitcherCloseHandlerRef). Something like:

    window.addEventListener('close-lang-switcher', handleCloseLangSwitcher);
    cleanups.push(() =>
      window.removeEventListener('close-lang-switcher', handleCloseLangSwitcher)
    );

Once DCO passes and conflicts are resolved, this is good to merge.

@clubanderson
Copy link
Copy Markdown
Contributor

Thanks for this — the CustomEvent pattern is the right direction and matches what #1471 is also cleaning up (they overlap the same closeLangSwitcher window-global area of Navbar.tsx). Two blockers right now:

  1. DCO sign-off missing — every commit on this branch needs a Signed-off-by: trailer. Fix by running git commit --amend -s && git push --force-with-lease (or for multi-commit branches, git rebase --signoff master). See the DCO check link for details.

  2. Merge conflict with mastermergeable: CONFLICTING. This likely overlaps with recent Navbar.tsx work. Please rebase on latest master and resolve.

Once both are resolved I'll /ok-to-test and merge. The lgtm label is already there.

@clubanderson
Copy link
Copy Markdown
Contributor

Thanks for the fix! The code approach (CustomEvent over global window function) looks good.

The PR is blocked on DCO sign-off. To fix this, please amend your commit(s) to include the sign-off:

git commit --amend -s
git push --force-with-lease

This adds a Signed-off-by: line to your commit message, which is required by the Developer Certificate of Origin.

@clubanderson
Copy link
Copy Markdown
Contributor

Thanks for the fix! Two things to address before this can merge:

  1. DCO: Your commits are missing the Signed-off-by line. Please amend them with git commit --amend -s (for the latest) or rebase with -s to add it to all commits.
  2. Merge conflict: The PR has a merge conflict with main. Please rebase onto the latest main and force-push.

Once those are resolved, this should be good to go.

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

Labels

dco-signoff: no Indicates the PR's author has not signed the DCO. frontend lgtm Indicates that a PR is ready to be merged. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Global Window Pollution via window.closeLangSwitcher for Cross-Component Communication

3 participants