Conversation
WalkthroughMakes Wave cards fully clickable and keyboard-accessible with router.push navigation, refactors contributor avatar rendering to show all contributors, updates related tests and tickets, adjusts small-screen max-heights in two drop components, and moves an html scrollbar-gutter rule into a media query. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Card as WaveItem (CardContainer)
participant Child as Interactive Descendant
participant Router as Next.js Router
User->>Card: Click card area
Card->>Card: compute waveHref & check target via INTERACTIVE_CHILD_SELECTOR
alt waveHref exists & not interactive child
Card->>Router: push(waveHref)
else event inside interactive child
Card-->>Child: no navigation (child handles event)
end
User->>Card: Press Enter / Space
alt Card focused & waveHref exists
Card->>Router: push(waveHref)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
components/waves/list/WaveItem.tsx (3)
103-114: Keydown handler: prefer event.code for Space (and include legacy "Spacebar")For role="link", Enter is standard; supporting Space is fine for cards, but check via event.code === "Space" or include "Spacebar" to cover older UAs. Current "Space" isn’t a standard key value.
- if (event.key === "Enter" || event.key === " " || event.key === "Space") { + if ( + event.key === "Enter" || + event.key === " " || + event.code === "Space" || + event.key === "Spacebar" + ) {
85-101: Broaden interactive-child guard to avoid accidental navigationInclude elements with role="button" in the closest() selector so clicks on custom buttons don’t trigger card navigation.
- target?.closest( - "a, button, input, textarea, select, [data-wave-item-interactive='true']" - ) + target?.closest( + "a, button, [role='button'], input, textarea, select, [data-wave-item-interactive='true']" + )
138-145: Optional: consider next/image for better image optimizationUsing next/image would improve lazy loading, responsive sizing, and priority hints. Not required for this PR, but worth tracking.
Also applies to: 177-193
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
__tests__/components/waves/list/WaveItem.test.tsx(3 hunks)codex/STATE.md(1 hunks)codex/tickets/TKT-0011.md(1 hunks)components/waves/drops/ArtistActiveSubmissionContent.tsx(1 hunks)components/waves/drops/ArtistWinningArtworksContent.tsx(1 hunks)components/waves/list/WaveItem.tsx(2 hunks)components/waves/list/WaveItemDropped.tsx(2 hunks)components/waves/list/header/WavesListHeader.tsx(1 hunks)styles/globals.scss(13 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript across the codebase
Files:
components/waves/list/header/WavesListHeader.tsxcomponents/waves/list/WaveItemDropped.tsxcomponents/waves/drops/ArtistActiveSubmissionContent.tsxcomponents/waves/drops/ArtistWinningArtworksContent.tsxcomponents/waves/list/WaveItem.tsx__tests__/components/waves/list/WaveItem.test.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks for UI components
Files:
components/waves/list/header/WavesListHeader.tsxcomponents/waves/list/WaveItemDropped.tsxcomponents/waves/drops/ArtistActiveSubmissionContent.tsxcomponents/waves/drops/ArtistWinningArtworksContent.tsxcomponents/waves/list/WaveItem.tsx__tests__/components/waves/list/WaveItem.test.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/list/WaveItem.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/list/WaveItem.test.tsx
**/__tests__/**
📄 CodeRabbit inference engine (AGENTS.md)
Place tests in
__tests__directories when organizing test suites
Files:
__tests__/components/waves/list/WaveItem.test.tsx
**/*.test.tsx
📄 CodeRabbit inference engine (AGENTS.md)
When colocating tests with components, name them
ComponentName.test.tsx
Files:
__tests__/components/waves/list/WaveItem.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/waves/list/WaveItem.test.tsx
🧬 Code graph analysis (3)
components/waves/list/WaveItemDropped.tsx (1)
helpers/Helpers.ts (1)
numberWithCommas(83-100)
components/waves/list/WaveItem.tsx (5)
helpers/Helpers.ts (2)
getRandomColorWithSeed(736-750)numberWithCommas(83-100)helpers/navigation.helpers.ts (1)
getWaveRoute(23-71)helpers/image.helpers.ts (1)
getScaledImageUri(14-42)components/waves/list/WaveItemDropped.tsx (1)
WaveItemDropped(6-50)components/waves/list/WaveItemFollow.tsx (1)
WaveItemFollow(22-186)
__tests__/components/waves/list/WaveItem.test.tsx (1)
components/waves/list/WaveItem.tsx (1)
WaveItem(29-284)
🪛 markdownlint-cli2 (0.18.1)
codex/tickets/TKT-0011.md
22-22: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
23-23: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
24-24: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
components/waves/list/header/WavesListHeader.tsx (1)
41-41: Verify removal of Tailwind styling is intentional.The h1 element now uses global styles from
globals.scssinstead of inline Tailwind classes. This deviates from the coding guideline to "Use TailwindCSS for styling" for.tsxfiles. Ensure this change aligns with the desired styling approach.codex/STATE.md (1)
17-17: LGTM!The new TKT-0011 entry follows the table format and includes all required fields.
styles/globals.scss (2)
190-194: Verify scrollbar-gutter media query change is intentional.The
scrollbar-gutter: stable both-edgesdeclaration was moved from applying globally to only screens withmin-width: 1300px. This changes layout behavior on smaller screens where the scrollbar gutter will no longer be reserved, potentially causing horizontal layout shifts when scrollbars appear/disappear.
22-23: LGTM on formatting changes.The reformatting of calc() expressions, gradients, and comments improves consistency without affecting behavior.
Also applies to: 82-93, 239-241, 559-560, 618-651, 661-670
__tests__/components/waves/list/WaveItem.test.tsx (1)
1-82: LGTM!The test updates comprehensively cover the new interactive card behavior:
- Navigation via card click
- Keyboard accessibility (Enter and Space keys)
- Guarded navigation that respects nested interactive elements
The mocks are appropriate and align with coding guidelines for testing.
components/waves/list/WaveItemDropped.tsx (1)
25-26: LGTM on performance attributes.Adding
loading="lazy"anddecoding="async"improves performance by deferring image loading and preventing blocking during decode.components/waves/list/WaveItem.tsx (2)
78-101: Interactive-card navigation and keyboard support look solidGood separation of concerns with navigateToWave + guarded click/keydown handlers; prevents nested control conflicts and ensures focusability.
Please verify tests cover:
- Click on whitespace of card navigates.
- Click on WaveItemFollow/links does not navigate.
- Keyboard Enter/Space on card navigates; on inner controls does not.
Also applies to: 103-114
29-37: Props marked readonly — nice adherence to typing guidelineThe props interface uses readonly consistently. No action needed.
There was a problem hiding this comment.
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/user/waves/UserPageWavesSearch.tsx (1)
86-86: Inconsistent icon spacing between adjacent buttons.Line 111 updates the Create DM icon margin to
tw-mr-2, but Line 86's Create Wave icon still usestw-mr-1.5. This creates visual inconsistency between two similar, adjacent buttons.Apply this diff to align the Create Wave icon margin:
- className="tw-size-5 tw-mr-1.5 -tw-ml-1 tw-flex-shrink-0" + className="tw-size-5 tw-mr-2 -tw-ml-1 tw-flex-shrink-0"Also applies to: 111-111
♻️ Duplicate comments (2)
components/waves/list/WaveItem.tsx (2)
3-7: Replace Heroicons with FontAwesome (coding guideline violation).This file violates the project guideline requiring FontAwesome for icons in
**/*.tsxfiles. The Heroicons imports should be replaced with FontAwesome equivalents.As per coding guidelines
Apply this diff:
-import { - ChatBubbleLeftRightIcon, - UsersIcon, -} from "@heroicons/react/24/outline"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faComments, faUsers } from "@fortawesome/free-solid-svg-icons";Then update the icon usages at lines 225–230 and 238–246:
- <ChatBubbleLeftRightIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faComments} + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + aria-hidden="true" + />- <UsersIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faUsers} + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + aria-hidden="true" + />
38-68: Guard author handle to avoid linking to "/undefined".At line 179, the code builds a link using
wave.author.handlewithout checking if the handle exists. Ifwave.author.handleis undefined, the href becomes"/undefined".Compute a safe
authorHrefand conditionally render the Link:const author = wave?.author; +const authorHref = author?.handle ? `/${author.handle}` : undefined;Then at lines 178–211, wrap the Link in a conditional:
- <Link - href={`/${wave.author.handle}`} + {authorHref ? ( + <Link + href={authorHref} prefetch={false} className="tw-mt-1 tw-group tw-no-underline tw-flex tw-flex-wrap tw-items-center tw-gap-x-2 tw-gap-y-1" > {/* ... avatar and name ... */} </Link> + ) : ( + <span className="tw-mt-1 tw-flex tw-flex-wrap tw-items-center tw-gap-x-2 tw-gap-y-1"> + {/* ... same avatar and name content ... */} + </span> + )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
__tests__/components/waves/Waves.test.tsx(2 hunks)__tests__/components/waves/list/WaveItemDropped.test.tsx(1 hunks)codex/STATE.md(1 hunks)codex/tickets/TKT-0011.md(1 hunks)codex/tickets/TKT-0016.md(1 hunks)codex/tickets/TKT-0017.md(1 hunks)components/brain/left-sidebar/web/WebDirectMessagesList.tsx(1 hunks)components/brain/left-sidebar/web/WebUnifiedWavesListWaves.tsx(5 hunks)components/messages/MessagesView.tsx(1 hunks)components/user/waves/UserPageWavesSearch.tsx(1 hunks)components/waves/list/WaveItem.tsx(2 hunks)components/waves/list/WaveItemDropped.tsx(1 hunks)components/waves/list/WaveItemFollow.tsx(1 hunks)components/waves/list/WavesListWrapper.tsx(1 hunks)components/waves/list/header/WavesListHeader.tsx(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- codex/STATE.md
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript across the codebase
Files:
components/brain/left-sidebar/web/WebDirectMessagesList.tsxcomponents/waves/list/WavesListWrapper.tsx__tests__/components/waves/list/WaveItemDropped.test.tsxcomponents/waves/list/WaveItemFollow.tsxcomponents/waves/list/WaveItemDropped.tsxcomponents/brain/left-sidebar/web/WebUnifiedWavesListWaves.tsx__tests__/components/waves/Waves.test.tsxcomponents/messages/MessagesView.tsxcomponents/user/waves/UserPageWavesSearch.tsxcomponents/waves/list/WaveItem.tsxcomponents/waves/list/header/WavesListHeader.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks for UI components
Files:
components/brain/left-sidebar/web/WebDirectMessagesList.tsxcomponents/waves/list/WavesListWrapper.tsx__tests__/components/waves/list/WaveItemDropped.test.tsxcomponents/waves/list/WaveItemFollow.tsxcomponents/waves/list/WaveItemDropped.tsxcomponents/brain/left-sidebar/web/WebUnifiedWavesListWaves.tsx__tests__/components/waves/Waves.test.tsxcomponents/messages/MessagesView.tsxcomponents/user/waves/UserPageWavesSearch.tsxcomponents/waves/list/WaveItem.tsxcomponents/waves/list/header/WavesListHeader.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/list/WaveItemDropped.test.tsx__tests__/components/waves/Waves.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/list/WaveItemDropped.test.tsx__tests__/components/waves/Waves.test.tsx
**/__tests__/**
📄 CodeRabbit inference engine (AGENTS.md)
Place tests in
__tests__directories when organizing test suites
Files:
__tests__/components/waves/list/WaveItemDropped.test.tsx__tests__/components/waves/Waves.test.tsx
**/*.test.tsx
📄 CodeRabbit inference engine (AGENTS.md)
When colocating tests with components, name them
ComponentName.test.tsx
Files:
__tests__/components/waves/list/WaveItemDropped.test.tsx__tests__/components/waves/Waves.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/waves/list/WaveItemDropped.test.tsx__tests__/components/waves/Waves.test.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-23T06:36:34.125Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: AGENTS.md:0-0
Timestamp: 2025-10-23T06:36:34.125Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests
Applied to files:
__tests__/components/waves/Waves.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
PR: 6529-Collections/6529seize-frontend#0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios
Applied to files:
__tests__/components/waves/Waves.test.tsx
🧬 Code graph analysis (4)
__tests__/components/waves/list/WaveItemDropped.test.tsx (1)
components/waves/list/WaveItemDropped.tsx (1)
WaveItemDropped(6-60)
components/waves/list/WaveItemDropped.tsx (2)
helpers/image.helpers.ts (1)
getScaledImageUri(14-42)helpers/Helpers.ts (1)
numberWithCommas(83-100)
__tests__/components/waves/Waves.test.tsx (1)
hooks/useDeviceInfo.ts (1)
useDeviceInfo(22-78)
components/waves/list/WaveItem.tsx (5)
helpers/Helpers.ts (2)
getRandomColorWithSeed(736-750)numberWithCommas(83-100)helpers/navigation.helpers.ts (1)
getWaveRoute(23-71)helpers/image.helpers.ts (1)
getScaledImageUri(14-42)components/waves/list/WaveItemDropped.tsx (1)
WaveItemDropped(6-60)components/waves/list/WaveItemFollow.tsx (1)
WaveItemFollow(22-186)
🪛 LanguageTool
codex/tickets/TKT-0017.md
[grammar] ~34-~34: Ensure spelling is correct
Context: ...- 2025-10-27T14:10:00Z – Documented the flakey Waves tests and logged the need for dev...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (21)
components/messages/MessagesView.tsx (1)
54-54: LGTM! Consistent label abbreviation.The button label change from "New Direct Message" to "Create DM" improves brevity and aligns with similar changes elsewhere in the PR (e.g., WavesListHeader.tsx).
codex/tickets/TKT-0011.md (1)
48-48: LGTM! Standard file formatting.The trailing blank line is a standard practice for text files and has no impact on the documentation content.
components/waves/list/WaveItemFollow.tsx (1)
149-149: Verify touch target size remains adequate.The padding reduction from
tw-px-3 tw-py-2totw-px-2.5 tw-py-1.5decreases both dimensions. Ensure the final button size (including the gradient wrapper padding) meets the minimum 44×44px touch target size for mobile accessibility.components/brain/left-sidebar/web/WebDirectMessagesList.tsx (1)
182-182: LGTM! Consistent padding standardization.The padding update to
"tw-p-2.5"aligns with similar changes in WebUnifiedWavesListWaves.tsx and provides a slightly larger, more accessible hit area.components/waves/list/WavesListWrapper.tsx (1)
217-217: Verify card appearance at the lg breakpoint.The change from
xl:tw-grid-cols-3tolg:tw-grid-cols-3activates the three-column layout at 1024px instead of 1280px. Please verify that Wave cards render properly at the narrower column width, especially regarding text truncation and image sizing.components/brain/left-sidebar/web/WebUnifiedWavesListWaves.tsx (2)
97-97: LGTM! Improved variable naming.The rename from
shouldRenderCreateWaveButtontoshowCreateWaveButtonis more concise while maintaining clarity. All references are consistently updated.Also applies to: 132-132, 156-156
142-142: LGTM! Consistent padding standardization.The padding updates to
"tw-p-2.5"match the standardization applied in WebDirectMessagesList.tsx, improving consistency and accessibility across button components.Also applies to: 165-165
components/waves/list/header/WavesListHeader.tsx (2)
92-92: Note: Icon spacing differs between buttons.The Create DM button icon uses
tw-mr-2, while the Create Wave button icon (line 69) usestw-mr-1.5. This may be intentional to account for the different icon sizes (tw-size-4vs.tw-size-5), but verify that the visual spacing appears balanced.
41-41: Global h1 styles are properly defined and provide appropriate styling.The global stylesheet at
styles/globals.scssincludes h1 style definitions (line 285–289) withfont-size: 20px,font-weight: 700, and color from variables, plus responsive styling for mobile screens. The code change is safe.__tests__/components/waves/Waves.test.tsx (1)
41-41: LGTM! Improved test mock management.The centralized
deviceInfoMockreference improves test maintainability and type safety. The default mock state inbeforeEachand per-test overrides provide better control over test isolation.Minor note: The cleanup at line 131 is redundant since
beforeEachalready resets the mock, but it documents intent and has no harm.Also applies to: 49-49, 87-87, 93-93, 131-131
__tests__/components/waves/list/WaveItemDropped.test.tsx (1)
21-29: LGTM!The test correctly validates that all contributor avatars are rendered, aligning with the updated production component behavior.
codex/tickets/TKT-0016.md (1)
1-53: LGTM!The ticket documentation is well-structured and provides clear context, plan, and acceptance criteria for the Wave card interactivity feature.
components/waves/list/WaveItemDropped.tsx (3)
7-7: LGTM!Safe extraction of contributors with appropriate fallback to an empty array.
12-48: LGTM!The per-contributor avatar rendering correctly addresses previous review concerns:
- Unique keys now include identity, pfp, and index to prevent duplicates.
- Alt text gracefully handles missing contributor identity.
- Lazy loading and async decoding optimize performance.
- Conditional Link wrapping with
prefetch={false}is appropriate.
50-57: LGTM!The drops count layout correctly handles pluralization and uses appropriate responsive styling.
components/waves/list/WaveItem.tsx (6)
18-27: LGTM!The
LEVEL_CLASSESconstant is well-structured with proper readonly typing for level-based color mapping.
78-114: LGTM!The navigation event handlers correctly:
- Guard against missing
waveHref.- Prevent navigation when clicking interactive descendants.
- Handle keyboard accessibility (Enter and Space keys).
117-128: LGTM!The interactive card wrapper correctly:
- Applies conditional styling based on
isInteractive.- Sets appropriate ARIA attributes (
role,aria-label).- Manages focus and keyboard navigation with
tabIndex.- Includes proper spacing in className interpolation.
129-173: LGTM!The hero image section correctly:
- Applies gradient backgrounds with fallback colors.
- Lazy-loads images with appropriate scaling and async decoding.
- Conditionally renders the wave name as a Link when navigable.
- Provides proper ARIA labeling.
249-262: LGTM!The tooltip implementation for the followers count is properly configured with appropriate styling and positioning strategy.
265-281: LGTM!The bottom row correctly:
- Renders
WaveItemDroppedas a non-interactive subcomponent.- Marks
WaveItemFollowwithdata-wave-item-interactive="true"to prevent card-level navigation when interacting with the follow button.
Signed-off-by: ragnep <ragneinfo@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
components/waves/list/WaveItem.tsx (2)
10-13: Replace Heroicons with FontAwesome (repo guideline).Comply with TSX icon standard; swap Heroicons for FontAwesome.
-import { - ChatBubbleLeftRightIcon, - UsersIcon, -} from "@heroicons/react/24/outline"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faComments, faUsers } from "@fortawesome/free-solid-svg-icons";- <ChatBubbleLeftRightIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faComments} + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + aria-hidden="true" + />- <UsersIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faUsers} + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + aria-hidden="true" + />As per coding guidelines
Also applies to: 282-286, 295-298
120-123: Guard author handle to avoid linking to “/undefined”; render non-link fallback.Build authorHref safely and conditionally render Link/span.
const author = wave?.author; + const authorHref = author?.handle ? `/${author.handle}` : undefined; const router = useRouter(); const tooltipBaseId = useId();- {wave ? ( - <Link - href={`/${wave.author.handle}`} - prefetch={false} - className="tw-mt-1 tw-group/author tw-no-underline tw-flex tw-flex-wrap tw-items-center tw-gap-x-2 tw-gap-y-1" - > + {wave ? ( + authorHref ? ( + <Link + href={authorHref} + prefetch={false} + className="tw-mt-1 tw-group/author tw-no-underline tw-flex tw-flex-wrap tw-items-center tw-gap-x-2 tw-gap-y-1" + > <div className="tw-h-6 tw-w-6 tw-flex-shrink-0"> {wave?.author.pfp ? ( <img className="tw-h-full tw-w-full tw-rounded-md tw-object-cover tw-bg-iron-800 tw-ring-1 tw-ring-white/10 desktop-hover:group-hover/author:tw-ring-white/30 desktop-hover:group-hover/author:tw-ring-offset-1 desktop-hover:group-hover/author:tw-ring-offset-iron-950 tw-transition tw-duration-300 tw-ease-out" src={getScaledImageUri( wave.author.pfp, ImageScale.W_AUTO_H_50 )} alt={ wave?.author.handle ? `${wave.author.handle} avatar` : "Author avatar" } loading="lazy" decoding="async" /> ) : ( <div className="tw-h-full tw-w-full tw-rounded-md tw-bg-iron-800 tw-ring-1 tw-ring-white/10" /> )} </div> <span className="tw-text-sm tw-font-semibold tw-text-white desktop-hover:group-hover/author:tw-text-iron-400 tw-transition tw-duration-300 tw-ease-out"> {wave?.author.handle ?? userPlaceholder} </span> <div className={`${resolveLevelClasses( wave?.author.level )} tw-border-none tw-inline-flex tw-items-center tw-rounded-xl tw-bg-transparent tw-px-2 tw-py-1 tw-font-semibold tw-ring-2 tw-ring-inset tw-text-[0.625rem] tw-leading-3`} > Level {wave.author.level} </div> - </Link> + </Link> + ) : ( + <span className="tw-mt-1 tw-group/author tw-flex tw-flex-wrap tw-items-center tw-gap-x-2 tw-gap-y-1"> + <div className="tw-h-6 tw-w-6 tw-flex-shrink-0"> + {wave?.author.pfp ? ( + <img + className="tw-h-full tw-w-full tw-rounded-md tw-object-cover tw-bg-iron-800 tw-ring-1 tw-ring-white/10" + src={getScaledImageUri(wave.author.pfp, ImageScale.W_AUTO_H_50)} + alt="Author avatar" + loading="lazy" + decoding="async" + /> + ) : ( + <div className="tw-h-full tw-w-full tw-rounded-md tw-bg-iron-800 tw-ring-1 tw-ring-white/10" /> + )} + </div> + <span className="tw-text-sm tw-font-semibold tw-text-white"> + {wave?.author.handle ?? userPlaceholder} + </span> + <div + className={`${resolveLevelClasses( + wave?.author.level + )} tw-border-none tw-inline-flex tw-items-center tw-rounded-xl tw-bg-transparent tw-px-2 tw-py-1 tw-font-semibold tw-ring-2 tw-ring-inset tw-text-[0.625rem] tw-leading-3`} + > + Level {wave.author.level} + </div> + </span> + ) ) : (Also applies to: 231-277
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
components/waves/list/WaveItem.tsx(2 hunks)components/waves/list/WaveItemDropped.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript across the codebase
Files:
components/waves/list/WaveItem.tsxcomponents/waves/list/WaveItemDropped.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks for UI components
Files:
components/waves/list/WaveItem.tsxcomponents/waves/list/WaveItemDropped.tsx
🧬 Code graph analysis (2)
components/waves/list/WaveItem.tsx (5)
helpers/Helpers.ts (2)
getRandomColorWithSeed(736-750)numberWithCommas(83-100)helpers/navigation.helpers.ts (1)
getWaveRoute(23-71)helpers/image.helpers.ts (1)
getScaledImageUri(14-42)components/waves/list/WaveItemDropped.tsx (1)
WaveItemDropped(6-60)components/waves/list/WaveItemFollow.tsx (1)
WaveItemFollow(22-186)
components/waves/list/WaveItemDropped.tsx (2)
helpers/image.helpers.ts (1)
getScaledImageUri(14-42)helpers/Helpers.ts (1)
numberWithCommas(83-100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
components/waves/list/WaveItem.tsx (2)
11-13: Replace Heroicons with FontAwesome per coding guidelines.The imports still use
@heroicons/react/24/outlineinstead of FontAwesome, violating the project guideline for**/*.tsxfiles.Apply this diff:
-import { - ChatBubbleLeftRightIcon, - UsersIcon, -} from "@heroicons/react/24/outline"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faComments, faUsers } from "@fortawesome/free-solid-svg-icons";Then update the usages at lines 306-310 and 319-323:
- <ChatBubbleLeftRightIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faComments} + aria-hidden="true" + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + />- <UsersIcon - aria-hidden="true" - className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" - /> + <FontAwesomeIcon + icon={faUsers} + aria-hidden="true" + className="tw-h-5 tw-w-5 tw-flex-shrink-0 tw-text-iron-400" + />As per coding guidelines
65-79: Nested anchor elements create invalid HTML.The
CardContainerreturns a<Link>(anchor) when interactive, but the card content includes nested<Link>elements for the title (lines 281-287) and author (lines 191-202). This produces invalid HTML structure even though JavaScript prevents the default navigation behavior.Refactor
CardContainerto use a<div>withrole="link"and keyboard/click handlers:function CardContainer({ isInteractive, href, ariaLabel, onClick, onKeyDown, children, }: CardContainerProps) { const className = `${CARD_BASE_CLASSES} ${ isInteractive ? CARD_INTERACTIVE_CLASSES : "" }`; - if (isInteractive && href) { - return ( - <Link - href={href} - prefetch={false} - className={className} - aria-label={ariaLabel} - style={{ textDecoration: "none" }} - onClick={onClick} - onKeyDown={onKeyDown} - > - {children} - </Link> - ); - } - - return ( - <div className={className} aria-label={ariaLabel}> - {children} - </div> - ); + return ( + <div + className={className} + aria-label={ariaLabel} + role={isInteractive && href ? "link" : undefined} + tabIndex={isInteractive && href ? 0 : undefined} + onClick={onClick} + onKeyDown={onKeyDown} + > + {children} + </div> + ); }Then update the event handler types at lines 48-49:
type CardContainerProps = { readonly isInteractive: boolean; readonly href?: string; readonly ariaLabel?: string; - readonly onClick?: (event: MouseEvent<HTMLAnchorElement>) => void; - readonly onKeyDown?: (event: KeyboardEvent<HTMLAnchorElement>) => void; + readonly onClick?: (event: MouseEvent<HTMLElement>) => void; + readonly onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void; readonly children: ReactNode; };And update the handler signatures at lines 216 and 233:
- const handleCardClick = useCallback( - (event: MouseEvent<HTMLAnchorElement>) => { + const handleCardClick = useCallback( + (event: MouseEvent<HTMLElement>) => {- const handleCardKeyDown = useCallback( - (event: KeyboardEvent<HTMLAnchorElement>) => { + const handleCardKeyDown = useCallback( + (event: KeyboardEvent<HTMLElement>) => {
🧹 Nitpick comments (1)
components/waves/list/WaveItem.tsx (1)
237-237: Simplify keyboard key check.The check for
event.key === "Space"is unnecessary. The spacebar key returns" "(a single space character), not the string"Space". Checking for"Space"has no effect.Apply this diff:
- if (event.key === "Enter" || event.key === " " || event.key === "Space") { + if (event.key === "Enter" || event.key === " ") {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
__tests__/components/waves/list/WaveItemDropped.test.tsx(1 hunks)codex/STATE.md(1 hunks)codex/tickets/TKT-0016.md(0 hunks)codex/tickets/TKT-0018.md(1 hunks)components/waves/list/WaveItem.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- codex/tickets/TKT-0016.md
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/components/waves/list/WaveItemDropped.test.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before propsUse TypeScript across the codebase
Files:
components/waves/list/WaveItem.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for stylingUse React functional components with hooks for UI components
Files:
components/waves/list/WaveItem.tsx
🧬 Code graph analysis (1)
components/waves/list/WaveItem.tsx (5)
helpers/Helpers.ts (2)
getRandomColorWithSeed(736-750)numberWithCommas(83-100)helpers/navigation.helpers.ts (1)
getWaveRoute(23-71)helpers/image.helpers.ts (1)
getScaledImageUri(14-42)components/waves/list/WaveItemDropped.tsx (1)
WaveItemDropped(6-68)components/waves/list/WaveItemFollow.tsx (1)
WaveItemFollow(22-186)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)



Summary by CodeRabbit
New Features
Improvements
Tests
Documentation