Skip to content

Conversation

@Yash-kurosaki
Copy link

@Yash-kurosaki Yash-kurosaki commented Nov 20, 2025

This PR implements the floating docs navigation shortcuts as requested in issue #4629.

Summary

  • Added DocsNavigationShortcuts component
  • Integrated shortcuts into DocsLayout.tsx
  • Ensures keyboard navigation is accessible and visible
  • Tested locally using npm run dev

here is the screenshot :
image

Issue Reference

Fixes #4629

Summary by CodeRabbit

  • New Features
    • Added keyboard navigation shortcuts to seamlessly move between documentation pages using left and right arrow keys for faster browsing.
    • Introduced Previous and Next buttons in the documentation layout for enhanced page navigation.
    • Navigation controls are fully responsive and optimized for medium-sized screens and larger displays for better user experience.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Nov 20, 2025

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 64f03cb
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/691ee639bd84790007f0fd1e
😎 Deploy Preview https://deploy-preview-4632--asyncapi-website.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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Walkthrough

A new DocsNavigationShortcuts component was added to provide keyboard shortcuts (arrow keys) and persistent UI buttons for navigating between documentation pages. The component was integrated into DocsLayout and renders conditionally based on viewport size.

Changes

Cohort / File(s) Change Summary
New Navigation Component
components/navigation/DocsNavigationShortcuts.tsx
New React component implementing keyboard-based (arrow keys) and UI button navigation for docs. Computes previous/next page targets, attaches global keydown listener with editable element detection, renders fixed left/right navigation buttons with ARIA labels, uses Next.js Link for navigation, and leverages useMemo and useRouter for optimization. Hidden on small screens.
Layout Integration
components/layout/DocsLayout.tsx
Added import and rendering of DocsNavigationShortcuts component, passing post prop to enable previous/next navigation calculations.

Sequence Diagram

sequenceDiagram
    participant User
    participant Component as DocsNavigationShortcuts
    participant DOM as Event Listener
    participant Router as Next.js Router
    
    User->>DOM: Presses Arrow Key / Clicks Button
    DOM->>Component: onKeyDown / onClick
    Component->>Component: Check if focus on<br/>editable/interactive element
    alt Not Editing
        Component->>Component: Determine prevPage/<br/>nextPage target
        Component->>Router: Navigate via Link/Router
        Router->>User: Load next/prev doc page
    else Editing
        Component->>User: Ignore (suppress navigation)
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

  • New component is self-contained with focused responsibility (keyboard + UI navigation)
  • Integration point is straightforward (single import and component render in DocsLayout)
  • Standard React patterns (useRouter, useMemo, event listeners)
  • Logic for detecting editable/interactive elements is defensive

Areas to verify:

  • Keyboard event listener cleanup in useEffect (no memory leaks)
  • prevPage/nextPage computation logic and edge cases (first/last page handling)
  • Accessibility: ARIA labels, keyboard focus management, and screen reader announcements
  • Responsive behavior: CSS media queries ensuring visibility only on md+ screens
  • Interaction with editable elements: ensuring text inputs, textareas, and contentEditable are properly excluded

Poem

🐰 Arrow keys now dance through docs with flair,
Left and right navigation in the air,
No more scrolling to the bottom's grind,
Previous and next are easy to find!
Keyboard shortcuts, buttons bright and true—
Reading docs is smoother now, it's new!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add floating docs navigation shortcuts' clearly and concisely summarizes the main change: adding navigation shortcuts to the documentation pages.
Linked Issues check ✅ Passed The pull request implements all core requirements from issue #4629: visible UI arrow buttons (Previous/Next), keyboard navigation support (arrow keys), and uses existing docs hierarchy for navigation paths.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the floating docs navigation shortcuts feature. No unrelated or out-of-scope modifications are present in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@asyncapi-bot
Copy link
Contributor

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 43
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-4632--asyncapi-website.netlify.app/

Copy link
Contributor

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/layout/DocsLayout.tsx (1)

1-24: Fix import ordering per simple-import-sort to satisfy lint

The GitHub Actions run is failing with simple-import-sort/imports on Line 1. This is triggered by the new DocsNavigationShortcuts import disrupting the expected ordering/grouping.

Rather than hand-tuning the order, it’s safest to run the project’s autofix:

npm run lint -- --fix

(or the equivalent lint command for this repo)

This will reorder the imports according to the configured simple-import-sort rules and unblock CI.

🧹 Nitpick comments (2)
components/navigation/DocsNavigationShortcuts.tsx (2)

14-40: Optional: broaden the “interactive” guard to include focused links

The global ArrowLeft/ArrowRight handler correctly skips most interactive elements via INTERACTIVE_TAGS and isContentEditable. However, when focus is on a link (<a> rendered by next/link), arrow keys will still trigger page navigation.

If you’d prefer not to override arrow behavior when a link itself is focused, consider including 'a' in INTERACTIVE_TAGS:

-const INTERACTIVE_TAGS = new Set(['input', 'textarea', 'select', 'button']);
+const INTERACTIVE_TAGS = new Set(['input', 'textarea', 'select', 'button', 'a']);

This is mostly a UX choice; current behavior is reasonable, but this tweak avoids surprises for keyboard users who are focused on a link.


16-30: Add brief JSDoc for the new component (and optionally the handler) to satisfy lint warnings

The pipeline is flagging missing JSDoc on the exported component (Line 16) and the inner handler (Line 30). A minimal description is enough to satisfy the rule and document behavior:

+/**
+ * Floating previous/next docs navigation with global ArrowLeft/ArrowRight shortcuts.
+ */
 export default function DocsNavigationShortcuts({ post }: IDocsNavigationShortcutsProps) {
@@
-    function handleKeyDown(event: KeyboardEvent) {
+    /**
+     * Handles global ArrowLeft/ArrowRight presses to navigate between docs pages.
+     */
+    function handleKeyDown(event: KeyboardEvent) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85ce754 and 64f03cb.

📒 Files selected for processing (2)
  • components/layout/DocsLayout.tsx (2 hunks)
  • components/navigation/DocsNavigationShortcuts.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-10-11T11:32:30.226Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:95-119
Timestamp: 2024-10-11T11:32:30.226Z
Learning: In the `BlogPostItem` component (`components/navigation/BlogPostItem.tsx`), nesting `<a>` tags inside the parent `Link` component leads to hydration issues; therefore, we should avoid nesting `<a>` tags inside `Link` components in this component.

Applied to files:

  • components/layout/DocsLayout.tsx
  • components/navigation/DocsNavigationShortcuts.tsx
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/layout/DocsLayout.tsx
  • components/navigation/DocsNavigationShortcuts.tsx
📚 Learning: 2024-10-11T07:38:35.745Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.

Applied to files:

  • components/navigation/DocsNavigationShortcuts.tsx
🧬 Code graph analysis (2)
components/layout/DocsLayout.tsx (1)
components/navigation/DocsNavigationShortcuts.tsx (1)
  • DocsNavigationShortcuts (16-89)
components/navigation/DocsNavigationShortcuts.tsx (1)
components/icons/ArrowLeft.tsx (1)
  • ArrowLeft (7-17)
🪛 GitHub Actions: PR testing - if Node project
components/layout/DocsLayout.tsx

[error] 1-1: Run autofix to sort these imports! simple-import-sort/imports

components/navigation/DocsNavigationShortcuts.tsx

[warning] 16-16: Missing JSDoc comment.


[error] 22-22: Prettier formatting: Delete , and Unexpected trailing comma.


[error] 24-24: Prettier formatting: Delete , and Unexpected trailing comma.


[warning] 30-30: Missing JSDoc comment.


[warning] 51-51: Padding and formatting rule: Expected blank line before this statement.


[warning] 51-51: Consistent return: Arrow function should return a value or explicitly return null.

⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lighthouse CI
🔇 Additional comments (3)
components/navigation/DocsNavigationShortcuts.tsx (2)

54-88: Floating nav UI and accessibility look solid

The floating prev/next buttons are well integrated: they’re hidden on small screens (hidden md:flex), use pointer-events-none on the container with pointer-events-auto on the buttons, and expose clear aria-labels plus visible titles/labels for different breakpoints. This should provide a good UX with minimal overlap/interaction issues.


19-25: Fix Prettier trailing commas in useMemo to unblock CI

The pipeline is failing on the trailing commas in the useMemo block (object literal and dependency array). Adjusting them to match the repo’s Prettier config should clear the errors.

  const navigationTargets = useMemo(
    () => ({
      prev: post?.prevPage?.href,
-     next: post?.nextPage?.href,
+     next: post?.nextPage?.href
    }),
-   [post?.prevPage?.href, post?.nextPage?.href],
+   [post?.prevPage?.href, post?.nextPage?.href]
  );
⛔ Skipped due to learnings
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:95-119
Timestamp: 2024-10-11T11:32:30.226Z
Learning: In the `BlogPostItem` component (`components/navigation/BlogPostItem.tsx`), nesting `<a>` tags inside the parent `Link` component leads to hydration issues; therefore, we should avoid nesting `<a>` tags inside `Link` components in this component.
components/layout/DocsLayout.tsx (1)

121-131: Good integration point for DocsNavigationShortcuts in the layout

Placing <DocsNavigationShortcuts post={post} /> at the top of the <main> (which is already position: relative) is a good choice: the component itself uses a fixed, centered overlay and handles visibility by breakpoint, so it doesn’t interfere with the existing mobile menu or TOC layout. Passing post directly keeps prev/next resolution aligned with the rest of the docs UI.

Comment on lines +27 to +52
useEffect(() => {
if (!navigationTargets.prev && !navigationTargets.next) return;

function handleKeyDown(event: KeyboardEvent) {
if (event.defaultPrevented) return;
if (event.metaKey || event.ctrlKey || event.altKey) return;
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;

const target = event.target as HTMLElement | null;
const tagName = target?.tagName?.toLowerCase();
const isEditable = target?.isContentEditable;

if (isEditable || (tagName && INTERACTIVE_TAGS.has(tagName))) return;

if (event.key === 'ArrowLeft' && navigationTargets.prev) {
router.push(navigationTargets.prev);
event.preventDefault();
} else if (event.key === 'ArrowRight' && navigationTargets.next) {
router.push(navigationTargets.next);
event.preventDefault();
}
}

window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [navigationTargets, router]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Refine useEffect for consistent-return + padding rules and clearer cleanup

The early return plus returning a cleanup function in another path is triggering the consistent-return warning, and the linter also wants a blank line before the cleanup return. You can address both and make the cleanup more explicit like this:

  useEffect(() => {
-    if (!navigationTargets.prev && !navigationTargets.next) return;
+    if (!navigationTargets.prev && !navigationTargets.next) {
+      return undefined;
+    }
@@
-    window.addEventListener('keydown', handleKeyDown);
-    return () => window.removeEventListener('keydown', handleKeyDown);
+    window.addEventListener('keydown', handleKeyDown);
+
+    return () => {
+      window.removeEventListener('keydown', handleKeyDown);
+    };
   }, [navigationTargets, router]);

This keeps behavior the same while satisfying the lint rules and making the lifecycle a bit clearer.

📝 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
useEffect(() => {
if (!navigationTargets.prev && !navigationTargets.next) return;
function handleKeyDown(event: KeyboardEvent) {
if (event.defaultPrevented) return;
if (event.metaKey || event.ctrlKey || event.altKey) return;
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
const target = event.target as HTMLElement | null;
const tagName = target?.tagName?.toLowerCase();
const isEditable = target?.isContentEditable;
if (isEditable || (tagName && INTERACTIVE_TAGS.has(tagName))) return;
if (event.key === 'ArrowLeft' && navigationTargets.prev) {
router.push(navigationTargets.prev);
event.preventDefault();
} else if (event.key === 'ArrowRight' && navigationTargets.next) {
router.push(navigationTargets.next);
event.preventDefault();
}
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [navigationTargets, router]);
useEffect(() => {
if (!navigationTargets.prev && !navigationTargets.next) {
return undefined;
}
function handleKeyDown(event: KeyboardEvent) {
if (event.defaultPrevented) return;
if (event.metaKey || event.ctrlKey || event.altKey) return;
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
const target = event.target as HTMLElement | null;
const tagName = target?.tagName?.toLowerCase();
const isEditable = target?.isContentEditable;
if (isEditable || (tagName && INTERACTIVE_TAGS.has(tagName))) return;
if (event.key === 'ArrowLeft' && navigationTargets.prev) {
router.push(navigationTargets.prev);
event.preventDefault();
} else if (event.key === 'ArrowRight' && navigationTargets.next) {
router.push(navigationTargets.next);
event.preventDefault();
}
}
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [navigationTargets, router]);
🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[warning] 30-30: Missing JSDoc comment.


[warning] 51-51: Padding and formatting rule: Expected blank line before this statement.


[warning] 51-51: Consistent return: Arrow function should return a value or explicitly return null.

🤖 Prompt for AI Agents
In components/navigation/DocsNavigationShortcuts.tsx around lines 27 to 52, the
effect currently early-returns in one path and returns a cleanup function in
another which trips the consistent-return rule and the linter wants a blank line
before the cleanup return; change the early exit to return an explicit no-op
cleanup (e.g., return () => {}), add a blank line immediately before the final
return that removes the event listener, and keep the rest of the handler logic
intact so the effect always returns a function and the cleanup is clearly
separated.

@bandantonio
Copy link
Collaborator

I would vote against having floating buttons that overlap with the main content. This is kinda awful UX.
Furthermore, the original issuer didn't mention that the buttons must be floating.

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.

[FEATURE] Add keyboard + UI arrow navigation (← →) for Previous/Next Docs Pages

3 participants