Skip to content

feat: Add scroll-to-top button component (#340)#815

Open
HNR138 wants to merge 3 commits intoSolFoundry:mainfrom
HNR138:main
Open

feat: Add scroll-to-top button component (#340)#815
HNR138 wants to merge 3 commits intoSolFoundry:mainfrom
HNR138:main

Conversation

@HNR138
Copy link

@HNR138 HNR138 commented Mar 24, 2026

Description

Closes #

Solana Wallet for Payout

**Wallet:8dgSUWh7Bm3VWkeac7py5xBoePGjiHG1j7eoYKoVboJr

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • 🎨 Style/UI update
  • ♻️ Code refactoring
  • ⚡ Performance improvement
  • ✅ Test addition/update

Checklist

  • Code is clean and follows the issue spec exactly
  • One PR per bounty (no multiple bounties in one PR)
  • Tests included for new functionality
  • All existing tests pass
  • No console.log or debugging code left behind
  • No hardcoded secrets or API keys

Testing

  • Manual testing performed
  • Unit tests added/updated
  • Integration tests added/updated

Screenshots (if applicable)

Additional Notes


@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

Adds a new client-only React component ScrollToTop (default export) that tracks window scroll position, toggles isVisible when window.pageYOffset exceeds 300, registers a passive scroll listener on mount and removes it on unmount, and renders a fixed circular button with aria-label="Scroll to top" and an ArrowUp icon which on click calls window.scrollTo({ top: 0, behavior: 'smooth' }). Also adds a root package.json declaring a lucide-react dependency (^1.0.1).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

awaiting-wallet

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is a template with no meaningful content describing the changeset; it lacks issue reference and actual implementation details. Provide a clear description of what the ScrollToTop component does, why it was added, and how it works. Reference the closed issue number and fill in relevant checklist items.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a scroll-to-top button component, which matches the primary implementation in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/common/ScrollToTop.tsx`:
- Around line 18-19: The scroll listener in the ScrollToTop component attaches
toggleVisibility without passive options; update the event registration to use a
passive option to improve scroll responsiveness by passing an options object
(e.g., { passive: true }) when calling window.addEventListener('scroll',
toggleVisibility, options) and ensure you remove the listener with the same
options object in the cleanup (window.removeEventListener('scroll',
toggleVisibility, options)); locate this change in the ScrollToTop component
where toggleVisibility is referenced.
- Around line 9-20: The ScrollToTop component's useEffect currently only sets
isVisible in the scroll event handler, so the initial visibility can be wrong
when the page mounts at an offset >300px; modify the effect that defines
toggleVisibility (the useEffect containing toggleVisibility,
window.addEventListener, and cleanup) to call toggleVisibility once immediately
on mount (before adding the event listener) so setIsVisible is initialized
correctly; keep the existing window.addEventListener('scroll', toggleVisibility)
and the cleanup removing the listener.
- Around line 36-37: Remove the redundant tabIndex={0} prop from the native
<button> in the ScrollToTop component
(frontend/src/components/common/ScrollToTop.tsx) — locate the JSX button element
that currently includes tabIndex={0} and delete that attribute so the element
relies on default keyboard-focusable semantics; confirm no tests or custom logic
depend on an explicit tabIndex and run basic accessibility checks to ensure
focus order remains correct.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aa9fdd30-f0d2-4d9d-b630-d17a516a7eec

📥 Commits

Reviewing files that changed from the base of the PR and between 72d63b0 and 8492880.

📒 Files selected for processing (1)
  • frontend/src/components/common/ScrollToTop.tsx

Comment on lines +18 to +19
window.addEventListener('scroll', toggleVisibility)
return () => window.removeEventListener('scroll', toggleVisibility)
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Use a passive scroll listener for better scroll responsiveness.

On Line 18-Line 19, the scroll handler is attached without passive listener options. For scroll-heavy pages, this can degrade perceived scroll performance on the main thread.

As per coding guidelines, frontend/**: React/TypeScript frontend. Check: Component structure and state management.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/common/ScrollToTop.tsx` around lines 18 - 19, The
scroll listener in the ScrollToTop component attaches toggleVisibility without
passive options; update the event registration to use a passive option to
improve scroll responsiveness by passing an options object (e.g., { passive:
true }) when calling window.addEventListener('scroll', toggleVisibility,
options) and ensure you remove the listener with the same options object in the
cleanup (window.removeEventListener('scroll', toggleVisibility, options));
locate this change in the ScrollToTop component where toggleVisibility is
referenced.

… add initial check, remove redundant tabIndex
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/common/ScrollToTop.tsx`:
- Around line 9-15: The toggleVisibility function in ScrollToTop currently uses
the deprecated window.pageYOffset; update it to use window.scrollY instead
(inside the useCallback where setIsVisible is called) so the check becomes
window.scrollY > 300, leaving the rest of the logic and dependencies unchanged;
ensure the reference appears in the toggleVisibility function and any related
scroll handler cleanup remains correct.
- Around line 21-22: The scroll listener is added with an options object but
removed without it, which can prevent removal in some environments; update the
ScrollToTop component to use a single options constant (e.g., const
scrollOptions = { passive: true }) and pass that same scrollOptions to both
window.addEventListener('scroll', toggleVisibility, scrollOptions) and
window.removeEventListener('scroll', toggleVisibility, scrollOptions) so the
listener matches correctly when cleaned up.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e2978c3a-a451-433f-9f23-22897d4e0478

📥 Commits

Reviewing files that changed from the base of the PR and between 8492880 and a2b6f68.

📒 Files selected for processing (1)
  • frontend/src/components/common/ScrollToTop.tsx

Comment on lines +9 to +15
const toggleVisibility = useCallback(() => {
if (window.pageYOffset > 300) {
setIsVisible(true)
} else {
setIsVisible(false)
}
}, [])
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider replacing deprecated pageYOffset with scrollY.

window.pageYOffset is a legacy alias that has been deprecated in favor of window.scrollY. Both work identically, but using the modern API is preferred for forward compatibility.

♻️ Suggested refactor
   const toggleVisibility = useCallback(() => {
-    if (window.pageYOffset > 300) {
+    if (window.scrollY > 300) {
       setIsVisible(true)
     } else {
       setIsVisible(false)
     }
   }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const toggleVisibility = useCallback(() => {
if (window.pageYOffset > 300) {
setIsVisible(true)
} else {
setIsVisible(false)
}
}, [])
const toggleVisibility = useCallback(() => {
if (window.scrollY > 300) {
setIsVisible(true)
} else {
setIsVisible(false)
}
}, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/common/ScrollToTop.tsx` around lines 9 - 15, The
toggleVisibility function in ScrollToTop currently uses the deprecated
window.pageYOffset; update it to use window.scrollY instead (inside the
useCallback where setIsVisible is called) so the check becomes window.scrollY >
300, leaving the rest of the logic and dependencies unchanged; ensure the
reference appears in the toggleVisibility function and any related scroll
handler cleanup remains correct.

Comment on lines +21 to +22
window.addEventListener('scroll', toggleVisibility, { passive: true })
return () => window.removeEventListener('scroll', toggleVisibility)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Event listener cleanup options mismatch with registration.

On Line 21, the scroll listener is registered with { passive: true } options, but on Line 22, removeEventListener is called without passing the same options object. While modern browsers typically match listeners by function reference alone for removal, this inconsistency can cause the listener to not be properly removed in some older browser implementations and contradicts the pattern established for passive listeners.

🔧 Proposed fix
   useEffect(() => {
     // Check initial scroll position
     toggleVisibility()
 
-    window.addEventListener('scroll', toggleVisibility, { passive: true })
-    return () => window.removeEventListener('scroll', toggleVisibility)
+    const options = { passive: true }
+    window.addEventListener('scroll', toggleVisibility, options)
+    return () => window.removeEventListener('scroll', toggleVisibility, options)
   }, [toggleVisibility])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
window.addEventListener('scroll', toggleVisibility, { passive: true })
return () => window.removeEventListener('scroll', toggleVisibility)
useEffect(() => {
// Check initial scroll position
toggleVisibility()
const options = { passive: true }
window.addEventListener('scroll', toggleVisibility, options)
return () => window.removeEventListener('scroll', toggleVisibility, options)
}, [toggleVisibility])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/common/ScrollToTop.tsx` around lines 21 - 22, The
scroll listener is added with an options object but removed without it, which
can prevent removal in some environments; update the ScrollToTop component to
use a single options constant (e.g., const scrollOptions = { passive: true })
and pass that same scrollOptions to both window.addEventListener('scroll',
toggleVisibility, scrollOptions) and window.removeEventListener('scroll',
toggleVisibility, scrollOptions) so the listener matches correctly when cleaned
up.

- Add ScrollToTop component at frontend/src/components/common/ScrollToTop.tsx
- Button appears when scrolling down more than 300px
- Smooth scroll animation to top on click
- Fade-in/fade-out animations
- Dark theme styling with indigo-600
- Accessible with aria-label and keyboard focusable
- No new dependencies, uses CSS transitions

Closes SolFoundry#340
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.

1 participant