-
-
Notifications
You must be signed in to change notification settings - Fork 11
765-fix: Fix styles active menu item #814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Lighthouse Report:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keyboard navigation of submenus seems broken to me. First Enter/Space
press redirects the page, second opens the menu
2025-03-15.12-16-29.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lighthouse Report:
|
β¦ip and courses pages
π WalkthroughWalkthroughThis update refactors navigation menus and related UI components. It introduces new menu layouts, improves accessibility and keyboard interaction, adds new color variables and mixins, updates donation data structures, and enhances test coverage. Several components receive new props or styling, and menu logic shifts to a click-to-open model with improved active state visuals. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant NavItem
participant DropdownWrapper
participant useOutsideClick
participant Router
User->>NavItem: Clicks menu button
NavItem->>NavItem: Toggle dropdown state
NavItem->>DropdownWrapper: Render dropdown (isOpen)
NavItem->>useOutsideClick: Listen for outside clicks
useOutsideClick-->>NavItem: On outside click, close dropdown
User->>NavItem: Presses Enter/Space/Escape
NavItem->>NavItem: Handle keyboard navigation
NavItem->>Router: Navigate if not dropdown
Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNo out-of-scope changes detected. Possibly related PRs
Suggested labels
Suggested reviewers
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. π§ ESLint
npm error Exit handler never called! π Recent review detailsConfiguration used: .coderabbit.yaml π Files selected for processing (2)
π§ Files skipped from review as they are similar to previous changes (2)
β° Context from checks skipped due to timeout of 90000ms (2)
β¨ Finishing Touches
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. πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this 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
π Outside diff range comments (1)
src/widgets/header/header.module.scss (1)
91-100
: π‘ Verification agentπ§© Analysis chain
Verify mobile menu opacity transition consistency.
The opacity transitions should align with the mobile menu open/close behavior. Ensure the opacity values work correctly with the transform animations.
π Script executed:
#!/bin/bash # Description: Check mobile menu interaction patterns and transition consistency # Expected: Find mobile menu state management and transition usage rg -A 5 -B 5 'mobile.*menu.*open\|\.open.*mobile' src/widgets/ ast-grep --pattern 'className={$$$mobile$$$}'Length of output: 112
π Script executed:
#!/bin/bash # Show SCSS context around .mobile-menu rules rg -n -C5 '\.mobile-menu' src/widgets/header/header.module.scss # Find where the mobile-menu class is toggled in the React component rg -n -C3 'mobile-menu' src/widgets/headerLength of output: 3036
Fix mobile menu display and opacity transitions
The
.mobile-menu.open
rule currently setsdisplay: none
, which prevents any transform or opacity animation from ever showing. To allow a smooth reveal:β’ In
src/widgets/header/header.module.scss
(around lines 70β100):
β Remove or overridedisplay: none
in the open state (e.g. usedisplay: block
).
β Ensure the closed state hasopacity: 0
and the open state hasopacity: 1
.
β Add an opacity transition alongside the transform.Suggested update:
.mobile-menu { - transform: translateY(-100%); + transform: translateY(-100%); + opacity: 0; + display: none; + transition: transform 0.4s ease-out, opacity 0.4s ease-out; } .mobile-menu.open { - transform: translateY(0); - display: none; - opacity: 1; + transform: translateY(0); + opacity: 1; + display: block; }
β»οΈ Duplicate comments (9)
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)
29-35
: Fix hardcoded alt text for dynamic icon usage.The alt text is hardcoded as "Donate-icon" but should be dynamic based on the icon being displayed, as previously suggested.
Apply this fix to make the alt text contextual:
- alt="Donate-icon" + alt={`${title} icon`}src/widgets/mobile-view/ui/mobile-view.module.scss (2)
4-12
: Remove default flex value and consider past feedback.The
align-items: flex-end
should be sufficient here without explicitly setting the defaultflex-start
behavior mentioned in past comments.
47-57
: Use color constants for border colors.The divider implementations use hard-coded color variables that should leverage existing constants as suggested in past feedback.
src/widgets/header/header.tsx (3)
8-8
: Import path needs updateThe import path for
donateOptions
should be from the data barrel export.
49-49
: Previous suggestion addressedThe removal of dynamic class names addresses the earlier suggestion about removing the white-class when only one color is used.
155-155
: Consider component decompositionThe Header component has grown significantly with the addition of the Support Us menu. Consider splitting each NavItem into separate components as previously suggested.
src/widgets/mobile-view/ui/mobile-view.tsx (2)
10-10
: Import path needs updateThe import path for
donateOptions
should be from the data barrel export.
242-242
: Component has grown significantlyAs suggested previously, consider splitting this large component into smaller, more manageable pieces. Each menu section could be its own component.
src/widgets/header/ui/nav-item/nav-item.module.scss (1)
115-115
: Consider using color constantThe hover color
$color-gray-450
should be defined as a constant as previously suggested.
π§Ή Nitpick comments (10)
src/shared/ui/logo/logo.module.scss (2)
3-4
: Consistent logo sizing across breakpoints
Default logo dimensions are now 48px but identical values are repeated in themediaβtabletβlarge
block. Consider extracting these into a variable or removing the redundant declarations to adhere to DRY principles.
12-13
: Redundant declarations
The width and height here mirror the default styles. Unless future breakpoints require different sizing, you can safely remove these rules.src/shared/hooks/use-outside-click/use-outside-click.ts (1)
3-7
: Consider using useCallback for the callback parameter.The hook works correctly, but including the callback in the dependency array could cause unnecessary re-renders if it's not memoized in consuming components.
Consider documenting that the callback should be wrapped with
useCallback
:+/** + * Hook to detect clicks outside a referenced element + * @param ref - Reference to the element + * @param callback - Function to call on outside click (should be memoized with useCallback) + * @param isDropdownOpen - Whether the dropdown is currently open + */ export function useOutsideClick<T extends HTMLElement>(src/widgets/breadcrumbs/ui/breadcrumbs.module.scss (1)
37-43
: Simplify padding declarations.The mobile breadcrumb styles look good, but the padding can be streamlined.
.mobile-breadcrumbs.breadcrumbs-content { @include media-tablet-large { width: 100%; - padding: 6px; - padding-left: 0; + padding: 6px 6px 6px 0; } }src/widgets/school-menu/ui/school-item/school-item.tsx (2)
28-28
: Consider using a more explicit check for non-clickable URLs.The
Boolean(url === '#')
could be simplified to justurl === '#'
since the comparison already returns a boolean.- const isNonClickable = Boolean(url === '#'); + const isNonClickable = url === '#';
31-31
: Potential className conflict issue.Using
cx(props.className)
may not properly merge class names ifprops.className
contains multiple classes. Consider usingcx(props.className)
only if it's a single class, or handle multiple classes appropriately.- <li {...props} className={cx(props.className)}> + <li {...props} className={cx(props.className)}>Actually, this should work correctly since
cx()
can handle multiple class names. The current implementation looks fine.src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)
39-45
: Remove redundant role attribute.The
role="button"
is redundant since the dropdown arrow is already contained within a<button>
element.<span className={cx('dropdown-arrow', { rotate: isDropdownActive })} - role="button" aria-expanded={isDropdownActive} >
src/widgets/mobile-view/ui/mobile-view.tsx (1)
216-228
: Consider alternative approach for non-interactive itemsThe descriptive text items use
url="#"
which creates non-functional links. Consider using a different component or prop to indicate these are informational items only.- <SchoolMenu.Item - className="support-title" - title="Your donations help us cover hosting, domains, licenses, and advertising for courses - and events. Every donation, big or small, helps!" - url="#" - color={color} - /> + <SchoolMenu.InfoItem + className="support-title" + title="Your donations help us cover hosting, domains, licenses, and advertising for courses + and events. Every donation, big or small, helps!" + color={color} + />src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (1)
55-68
: Consider the visual impact of the decorative elementThe white
::before
element might not be visible against light backgrounds. Consider if this provides the intended visual effect in all scenarios.src/widgets/header/ui/nav-item/nav-item.tsx (1)
127-130
: Consider moving aria-expanded to the parent buttonThe
aria-expanded
attribute on a non-interactive span might be confusing. Consider moving it to the parent button element for better accessibility semantics.- {isDropdown && ( - <span className={cx('dropdown-arrow')} role="button" aria-expanded={isDropdownOpen}> - <DropdownArrow /> - </span> - )} - </button> + aria-expanded={isDropdown ? isDropdownOpen : undefined} + > + {icon && ( + <Image + src={icon} + alt={`${label} icon`} + width={18} + height={16} + aria-hidden="true" + data-testid="nav-item-icon" + /> + )} + <span className={cx('label-bold')}> + {label} + <span className={cx('label-content')} aria-hidden="true"> + {label} + </span> + </span> + {isDropdown && ( + <span className={cx('dropdown-arrow')} aria-hidden="true"> + <DropdownArrow /> + </span> + )} + </button>
π Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (3)
src/shared/assets/svg/heart-blue.svg
is excluded by!**/*.svg
src/shared/assets/svg/heart-yellow.svg
is excluded by!**/*.svg
src/shared/assets/svg/rss-logo-blue.svg
is excluded by!**/*.svg
π Files selected for processing (30)
dev-data/donate-options.data.ts
(1 hunks)dev-data/school-menu-links.ts
(1 hunks)src/core/styles/_constants.scss
(1 hunks)src/core/styles/_mixins.scss
(1 hunks)src/shared/constants.ts
(2 hunks)src/shared/hooks/use-outside-click/use-outside-click.ts
(1 hunks)src/shared/ui/logo/logo.module.scss
(1 hunks)src/shared/ui/logo/logo.tsx
(2 hunks)src/shared/ui/paragraph/paragraph.module.scss
(2 hunks)src/shared/ui/paragraph/paragraph.tsx
(1 hunks)src/widgets/breadcrumbs/ui/breadcrumbs.module.scss
(1 hunks)src/widgets/breadcrumbs/ui/breadcrumbs.tsx
(2 hunks)src/widgets/footer/footer.test.tsx
(2 hunks)src/widgets/header/header.module.scss
(4 hunks)src/widgets/header/header.test.tsx
(5 hunks)src/widgets/header/header.tsx
(6 hunks)src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss
(2 hunks)src/widgets/header/ui/dropdown/dropdown-wrapper.tsx
(1 hunks)src/widgets/header/ui/nav-item/nav-item.module.scss
(3 hunks)src/widgets/header/ui/nav-item/nav-item.tsx
(2 hunks)src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss
(1 hunks)src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx
(1 hunks)src/widgets/mobile-view/ui/mobile-view.module.scss
(1 hunks)src/widgets/mobile-view/ui/mobile-view.tsx
(2 hunks)src/widgets/school-menu/ui/school-item/school-item.module.scss
(3 hunks)src/widgets/school-menu/ui/school-item/school-item.tsx
(1 hunks)src/widgets/school-menu/ui/school-menu/school-menu.module.scss
(2 hunks)src/widgets/school-menu/ui/school-menu/school-menu.test.tsx
(1 hunks)src/widgets/school-menu/ui/school-menu/school-menu.tsx
(1 hunks)src/widgets/support/ui/support.tsx
(1 hunks)
π§° Additional context used
𧬠Code Graph Analysis (4)
dev-data/school-menu-links.ts (2)
src/shared/constants.ts (1)
ANCHORS
(68-77)dev-data/index.ts (1)
communityMenuStaticLinks
(49-49)
src/widgets/school-menu/ui/school-item/school-item.tsx (1)
src/shared/ui/logo/logo.tsx (1)
cx
(17-17)
src/widgets/footer/footer.test.tsx (3)
src/shared/__tests__/utils/render-with-router/render-with-router.tsx (1)
renderWithRouter
(38-49)src/widgets/mobile-view/ui/mobile-view.tsx (1)
MobileView
(41-244)src/shared/__tests__/constants.ts (1)
mockedCourses
(27-207)
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)
src/shared/icons/dropdown-arrow.tsx (1)
DropdownArrow
(5-7)
π Additional comments (60)
src/shared/ui/paragraph/paragraph.module.scss (2)
18-22
: Confirm spacing requirements
Unlike the other size variants,.small-font-size
doesnβt include bottom padding. Verify against the design spec if vertical spacing is needed here.
35-37
: Review responsive adjustments
At themedia-laptop
breakpoint onlyline-height
is overridden. Confirm whether properties likefont-size
orfont-weight
should also adapt on larger screens.src/core/styles/_constants.scss (1)
56-59
: Addition of intermediate gray variables
Both$color-gray-450
and$color-gray-250
are correctly inserted into the grayscale scale and follow the existing ordering.src/core/styles/_mixins.scss (1)
3-7
: Newmedia-laptop-large
mixin
The mixin appropriately targets viewports above$content-width
. Ensure documentation or examples are updated to reflect this new breakpoint utility.src/shared/constants.ts (1)
45-49
: ExtendedKEY_CODES
for improved keyboard interactions
AddingENTER
andSPACE
aligns with accessibility best practices for menu toggles.src/shared/hooks/use-outside-click/use-outside-click.ts (1)
13-17
: Solid click detection logic.Good use of
mousedown
event and proper null checking before callingcontains()
.dev-data/donate-options.data.ts (1)
9-12
: Clean separation of button and menu variants.The split into
buttonLinkLabel
/menuLinkLabel
andbuttonIcon
/menuIcon
provides good flexibility for different UI contexts.src/widgets/school-menu/ui/school-menu/school-menu.test.tsx (1)
42-44
: Test expectations correctly updated.The increase from 2 to 4 elements aligns with the addition of new menu items mentioned in the PR objectives.
src/shared/ui/paragraph/paragraph.tsx (1)
18-18
: LGTM! Clean addition of new font size variant.The new "small" variant follows the established pattern and integrates well with the existing CVA configuration.
src/widgets/footer/footer.test.tsx (2)
10-10
: Appropriate mock addition for navigation-dependent components.The
usePathname
mock returning'/'
is consistent with the testing pattern and necessary for components using Next.js navigation hooks.
41-43
: Improved JSX formatting for better readability.The multi-line format makes the test props more readable while maintaining the same functionality.
dev-data/school-menu-links.ts (2)
14-23
: Well-structured addition of new menu items.Both "Support Us" and "Mentors Wanted" entries follow the established pattern perfectly, using proper anchor constants and providing clear descriptions.
28-28
: Improved clarity with more specific title.Changing "About" to "About Community" provides better context and reduces ambiguity when multiple "About" sections exist.
src/widgets/breadcrumbs/ui/breadcrumbs.tsx (3)
15-17
: Clean type definition for enhanced styling flexibility.The optional
className
prop follows React best practices and maintains backward compatibility.
19-19
: Proper component signature update.The destructured
className
parameter correctly implements the new prop interface.
31-31
: Correct implementation of className merging.The
cx()
function properly combines existing classes with the optional externalclassName
, maintaining styling consistency.src/widgets/support/ui/support.tsx (2)
31-31
:β Verification successful
Verify destructuring matches updated data structure.
The destructuring now includes
buttonLinkLabel
andbuttonIcon
instead of the previouslinkLabel
andicon
. Ensure thedonateOptions
data structure indev-data/donate-options.data.ts
exports these exact property names.
π Script executed:
#!/bin/bash # Description: Verify the donateOptions data structure has the expected properties # Expected: Properties should include buttonLinkLabel and buttonIcon rg -A 10 -B 5 "buttonLinkLabel|buttonIcon" --type tsLength of output: 2744
Verification complete: destructuring matches data structure
ThedonateOptions
array indev-data/donate-options.data.ts
exportsbuttonLinkLabel
andbuttonIcon
, matching the destructuring insupport.tsx
.
34-34
:β Verification successful
Verify icon function call pattern.
The
buttonIcon()
is called as a function. Ensure this returns a valid React component and that all icons in the data structure follow this pattern.
π Script executed:
#!/bin/bash # Description: Check how buttonIcon is defined in the donate options data # Expected: Should be functions that return React components ast-grep --pattern 'buttonIcon: $$$'Length of output: 38
π Script executed:
#!/bin/bash # Search for where buttonIcon is defined and how itβs assigned rg "buttonIcon" -n -C2 .Length of output: 1519
π Script executed:
#!/bin/bash # Inspect imports in donate-options data sed -n '1,50p' dev-data/donate-options.data.ts # Locate component definitions for the icons rg "export .*OpenCollectiveIcon" -n . rg "export .*BoostyIcon" -n .Length of output: 1124
Icon function call pattern verified
buttonIcon
maps to pure functional components (OpenCollectiveIcon
,BoostyIcon
) that return valid React elements and donβt use hooks, so callingbuttonIcon()
is safe.(Optional) For clearer JSX semantics you could destructure:
donateOptions.map(({ buttonIcon: Icon, ... }) => ( <Icon /> β¦ ))but no changes are required.
src/widgets/school-menu/ui/school-item/school-item.tsx (1)
39-39
: Good accessibility improvement with tabIndex management.Properly setting
tabIndex={-1}
for non-clickable items removes them from keyboard navigation, which improves accessibility and addresses potential keyboard navigation issues mentioned in the PR comments.src/widgets/school-menu/ui/school-item/school-item.module.scss (4)
3-4
: Good spacing adjustment for better visual hierarchy.Reducing the gap from 15px to 8px and changing alignment to
flex-start
creates better visual spacing and addresses the spacing inconsistencies mentioned in PR comments.
10-13
: Excellent accessibility enhancement for non-clickable items.The
.non-clickable
class properly disables pointer events and changes cursor, which works well with thetabIndex={-1}
set in the component for truly non-interactive items.
57-59
: Icon sizing constraint is well-implemented.Using the data-testid selector to constrain icon height to 32px ensures consistent icon sizing across the menu items.
32-32
:β Verification successful
Verify new color variable is defined.
The
$color-gray-450
variable is used but needs to be defined in the constants file.
π Script executed:
#!/bin/bash # Description: Check if the new gray color variable is defined # Expected: Should find $color-gray-450 definition rg "\$color-gray-450" --type scssLength of output: 63
To catch definitions without relying on filetype detection, letβs search all SCSS files by extension:
π Script executed:
#!/bin/bash # Search for any usage or definition of $color-gray-450 in .scss files rg '\$color-gray-450' -g '*.scss'Length of output: 292
Confirmed:
$color-gray-450
is defined
The variable is declared insrc/core/styles/_constants.scss
as:$color-gray-450: hsl(210deg 9% 62%); // #969fa8No further action needed.
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (4)
18-20
: Good dropdown arrow animation implementation.The rotation animation provides clear visual feedback for collapsible menu states, which should improve the user experience mentioned in PR objectives.
1-27
: Well-structured mobile navigation styling.The category link styling properly resets button defaults and provides good mobile touch targets with appropriate font sizing and spacing.
16-16
:β Verification successful
Verify transition mixin is defined.
The
@extend %transition-all
references a placeholder selector that should be defined in the shared styles.
π Script executed:
#!/bin/bash # Description: Check if the transition-all placeholder is defined # Expected: Should find %transition-all definition rg "%transition-all" --type scssLength of output: 61
π Script executed:
#!/bin/bash # Search for the %transition-all placeholder across all SCSS files rg '%transition-all' -g '*.scss' . || grep -R --include='*.scss' '%transition-all' .Length of output: 1570
%transition-all placeholder verified
The
%transition-all
placeholder is defined insrc/core/styles/_placeholders.scss
, so the@extend %transition-all;
inmobile-nav-item.module.scss
is valid. No further changes needed.
40-40
:β Verification successful
Verify light color variable consistency.
Using
$color-gray-100
for light text. Ensure this provides sufficient contrast for accessibility.
π Script executed:
#!/bin/bash # Description: Check the gray color palette definition # Expected: Should find consistent gray color variable definitions rg "\$color-gray-(100|250|450|900)" --type scssLength of output: 77
π Script executed:
#!/bin/bash # Re-run search for gray color variables across all SCSS files rg -g '*.scss' '\$color-gray-(100|250|450|900)' -nLength of output: 2854
π Script executed:
#!/bin/bash # Display mobile-nav-item styles to confirm background and text color context sed -n '1,200p' src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scssLength of output: 615
Color variable usage verified
Definitions for
$color-gray-100
(#f0f2f5
) and$color-gray-900
(#191b1d
) are consistent incore/styles/_constants.scss
, and the.light
/.dark
classes inmobile-nav-item.module.scss
correctly apply them. Contrast between these values meets WCAG AA/AAA on their intended backgrounds.β’ core/styles/_constants.scss (lines 51β61): gray palette
β’ src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (lines 36β40):.dark
/.light
classesNo changes required.
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)
18-47
: Well-structured mobile navigation component.The component properly handles conditional icon rendering, dropdown states, and accessibility attributes. The click handler and ARIA attributes support good UX and accessibility.
src/widgets/school-menu/ui/school-menu/school-menu.tsx (2)
15-16
: Good addition of layout and visibility props.These props address previous feedback about simplifying consumer component logic and moving complexity into the component itself.
28-36
: Proper implementation of conditional visibility and layout.The visibility classes are applied correctly based on the
isVisible
prop, and the dynamic layout class follows React best practices.src/widgets/header/ui/dropdown/dropdown-wrapper.tsx (2)
10-10
: Good addition of layout control prop.The
reverseLayout
prop provides flexibility for dropdown positioning without complex logic.
14-27
: Simplified component interface improves maintainability.Removing mouse event handlers and positioning logic simplifies the component. This suggests a shift to click-based dropdown interaction, which can improve mobile UX.
src/shared/ui/logo/logo.tsx (2)
12-15
: Good extension of props interface.Adding the optional
icon
prop with proper TypeScript typing maintains backward compatibility while enabling dynamic logo usage.
21-31
: Proper implementation of dynamic icon support.The default value ensures backward compatibility while the dynamic
src
attribute enables flexible logo rendering across different contexts.src/widgets/mobile-view/ui/mobile-view.module.scss (2)
31-34
: Well-structured media query organization.Good placement of
overflow-y: hidden
and explicitflex-direction: column
in the tablet-large media query, addressing the layout structure concerns.
14-28
: Excellent responsive breadcrumbs handling.The nested
.content.breadcrumbs-content
selector with tablet-specific padding removal is a clean approach for responsive design.src/widgets/header/header.test.tsx (4)
15-18
: Appropriate navigation mocking for header tests.The
next/navigation
mock properly stubs router functionality needed for the header component testing.
42-47
: Improved test scoping with within.Using
within(desktopMenu)
provides better test isolation and reduces flaky test potential compared to global queries.
90-90
: Good addition of reverseLayout prop.The
reverseLayout={false}
prop addition aligns with the component API changes and maintains test coverage for this feature.
68-74
:β Verification inconclusive
Verify mobile menu arrow count aligns with implementation.
The test expects 5 SVG arrows in mobile view. Ensure this count matches the actual mobile menu structure after the refactoring.
π Script executed:
#!/bin/bash # Description: Verify mobile menu dropdown arrow count in mobile-view components # Expected: Find mobile navigation items with dropdown arrows ast-grep --pattern 'aria-label="dropdown-arrow"' rg -A 3 -B 3 'dropdown-arrow' src/widgets/mobile-view/Length of output: 1986
π Script executed:
#!/bin/bash # Locate the DropdownArrow icon component and inspect for aria-label DROP_PATH=$(fd dropdown-arrow.tsx) echo "Inspecting file: $DROP_PATH" # Check for any aria attributes on the icon rg -n "aria" "$DROP_PATH" # Show the SVG markup to see if an aria-label is set rg -n "<svg" -A5 "$DROP_PATH"Length of output: 374
Verify mobile menu arrow count matches rendered items
I confirmed each
<MobileNavItem>
renders one icon witharia-label="dropdown-arrow"
. Please double-check the number of items passed into the mobile menu (insrc/widgets/header/header.tsx
where you render the items underdata-testid="mobile-menu"
) and update the testβs expected length if it no longer equals 5.src/widgets/school-menu/ui/school-menu/school-menu.module.scss (4)
2-2
: Good use of transition placeholder.The
@extend %transition-all
provides consistent transition behavior across the component.
29-39
: Sophisticated grid layout implementation.The grid-based approach with automatic column adjustment for 5+ items is well-designed for flexible menu layouts.
52-68
: Excellent visibility state implementation.The
.hidden
and.visible
classes properly use opacity constants and create smooth transitions for show/hide behavior.
35-38
:β Verification inconclusive
Verify grid layout behavior with variable content.
The
:has(:nth-child(5))
selector switches to single-column layout. Test this behavior with different menu item counts to ensure proper responsive behavior.
π Script executed:
#!/bin/bash # Description: Check school menu usage patterns to verify grid layout behavior # Expected: Find school menu components and their typical item counts rg -A 10 -B 5 'SchoolMenu.*items\|school.*menu.*data' src/ ast-grep --pattern 'SchoolMenu $$$'Length of output: 98
Verify grid layout behavior across varying menu item counts
Please ensure the single-column switch triggered by
:has(:nth-child(5))
works as intended:β’ Test the SchoolMenu component (in app, Storybook or tests) with fewer than 5 items, exactly 5 items, and more than 5 items.
β’ Confirm the grid switches back to multi-column when item count drops below 5.
β’ Validate responsive behavior at different breakpoints.We couldnβt find automated usage patterns in the repoβmanual or Storybook verification is recommended.
src/widgets/header/header.module.scss (4)
8-8
: Appropriate height increase for better visual hierarchy.The navbar height increase from 64px to 70px provides better proportions and aligns with the menu refactoring goals.
14-16
: Clean unified background styling.The consistent white background with subtle shadow creates a more cohesive header appearance compared to the previous color variants.
43-43
: Good use of gap for menu spacing.The increased gap (33.8px) between menu items addresses previous feedback about using gap instead of margins for layout.
107-117
: Well-structured support text styling.The
.support-text
class provides proper layout for donation-related content with appropriate constraints and typography.src/widgets/header/header.tsx (2)
35-38
: LGTM!Clean implementation of conditional logic for mentorship page detection and icon selection.
64-130
: Well-structured menu implementationGood use of NAV_MENU_LABELS constants and consistent SchoolMenu patterns across all navigation items.
src/widgets/mobile-view/ui/mobile-view.tsx (1)
49-68
: Excellent state management implementationGreat use of Set for managing dropdown states and proper cleanup when menu closes. The toggle logic is clean and efficient.
src/widgets/header/ui/nav-item/nav-item.module.scss (2)
22-30
: Great accessibility improvement!The addition of focus-visible styles with proper outline enhances keyboard navigation support.
53-74
: Clever solution for preventing layout shiftThe nested label structure with transparent parent effectively prevents layout shift when font weight changes between regular and bold states.
src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (3)
15-17
: CSS Grid implementation looks good!The three-column grid layout provides a clean structure for dropdown content organization.
29-29
: Proper use of hsla color syntaxGood implementation following the requested pattern for colors with opacity.
31-53
:β Verification successful
Verify browser support for :has() pseudo-class
The
:has()
pseudo-class has limited browser support. Ensure this aligns with your browser support requirements.
π Web query:
What is the current browser support for CSS :has() pseudo-class and when was it added to major browsers?
π‘ Result:
The CSS
:has()
pseudo-class, introduced in the Selectors Level 4 specification, allows developers to select elements based on their descendants, effectively enabling parent selection in CSS. (css-tricks.com)Browser Support Timeline:
Chrome: Full support began with version 105, released on September 2, 2022. (caniuse.com)
Edge: Support was added starting with version 105, released on September 1, 2022. (caniuse.com)
Safari: Support commenced with version 15.4, released on March 14, 2022. (caniuse.com)
Firefox: Support was introduced in version 121, released on December 19, 2023. (caniuse.com)
Current Support Status (as of May 27, 2025):
As of May 2025, the
:has()
pseudo-class is supported in the latest versions of major browsers:
Chrome: Versions 105 and later.
Edge: Versions 105 and later.
Safari: Versions 15.4 and later.
Firefox: Versions 121 and later.
This widespread support allows developers to utilize the
:has()
pseudo-class for more dynamic and context-aware styling across modern browsers.Citations:
- 1: https://css-tricks.com/almanac/pseudo-selectors/h/has/?utm_source=openai
- 2: https://caniuse.com/css-has?utm_source=openai
- 3: https://caniuse.com/css-has?utm_source=openai
- 4: https://caniuse.com/css-has?utm_source=openai
- 5: https://caniuse.com/css-has?utm_source=openai
Browser Support for CSS :has() Verified
The CSS
:has()
pseudo-class is supported in all modern browsers as of these versions:
- Chrome 105+ (Sep 2, 2022)
- Edge 105+ (Sep 1, 2022)
- Safari 15.4+ (Mar 14, 2022)
- Firefox 121+ (Dec 19, 2023)
As long as your projectβs browser-support baseline targets these (or later) versions, you can safely use
:has()
for dynamic grid layouts insrc/widgets/header/ui/dropdown/dropdown-wrapper.module.scss
(lines 31β53). No further changes required.src/widgets/header/ui/nav-item/nav-item.tsx (4)
48-53
: Good implementation of dropdown coordinationThe custom event pattern effectively ensures only one dropdown is open at a time.
70-76
: Excellent keyboard accessibility implementationProper handling of Enter, Space, and Escape keys with preventDefault for form submission prevention.
80-86
: Proper event listener lifecycle managementGood implementation with cleanup to prevent memory leaks.
120-125
:β Verification successful
Verify the double label rendering pattern
The duplicate label with
aria-hidden
appears to be for styling purposes. Ensure this doesn't cause issues with screen readers or SEO.
π Script executed:
#!/bin/bash # Check if this pattern is used elsewhere in the codebase ast-grep --pattern $'<span className={$_}> $_ <span className={$_} aria-hidden="true"> $_ </span> </span>'Length of output: 117
π Script executed:
#!/bin/bash # Search for all occurrences of aria-hidden to identify similar double-render patterns rg -n 'aria-hidden="true"'Length of output: 1107
Doubleβrender label pattern is consistent and accessible
I checked similar components (mobile-nav-item, school-item, etc.) and confirmed the duplicate label with aria-hidden="true" is a deliberate styling trick. Screen readers will ignore the hidden span, so only the primary label is announced and thereβs no SEO impact. No changes required.
I noticed that tapping the logo in the mobile menu doesn't trigger the onClose callback, so the collapsible menu remains open. Maybe we should add onClick={onClose} to the Logo component when it's used in the mobile menu to ensure consistent behavior. |
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
β»οΈ Duplicate comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)
86-105
: π οΈ Refactor suggestionExtract the repeated menu pattern into a reusable component.
The MobileNavItem + SchoolMenu pattern is duplicated multiple times. This addresses the past review feedback about creating reusable components.
Create a reusable
MobileMenuSection
component:type MobileMenuSectionProps = { title: string; icon?: StaticImageData; color: 'light' | 'dark'; items: Array<{ key: string; title: string; description?: string; url: string; icon?: StaticImageData; }>; isVisible: boolean; onToggle: () => void; onItemClick?: () => void; }; const MobileMenuSection = ({ title, icon, color, items, isVisible, onToggle, onItemClick }: MobileMenuSectionProps) => ( <div className={cx('category-container')}> <MobileNavItem title={title} icon={icon} color={color} isDropdownActive={isVisible} onMenuItemClick={onToggle} /> <SchoolMenu isVisible={isVisible}> {items.map((item) => ( <SchoolMenu.Item key={item.key} icon={item.icon} title={item.title} description={item.description} url={item.url} color={color} onClick={onItemClick} /> ))} </SchoolMenu> </div> );
40-243
: π οΈ Refactor suggestionConsider splitting this large component.
This component has grown quite large and handles multiple responsibilities. As mentioned in past reviews, it should be split into smaller components.
Consider extracting sections like:
MobileMenuHeader
(logo + breadcrumbs)MobileMenuContent
(all menu sections)- Individual menu sections as separate components
This would improve maintainability and testability while addressing the component size concern raised in previous reviews.
π§Ή Nitpick comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)
228-228
: Avoid creating new arrays on every render.The
toReversed()
call creates a new array on every render, which is unnecessary.- {donateOptions.toReversed().map((option) => ( + {[...donateOptions].reverse().map((option) => (Or better yet, reverse the array once outside the component or in useMemo:
+ const reversedDonateOptions = useMemo(() => [...donateOptions].reverse(), []); + // Later in JSX: - {donateOptions.toReversed().map((option) => ( + {reversedDonateOptions.map((option) => (
217-227
: Extract hard-coded support messages to constants.The support section contains hard-coded strings that should be moved to constants for maintainability.
+const SUPPORT_MESSAGES = { + DESCRIPTION: "Your donations help us cover hosting, domains, licenses, and advertising for courses and events. Every donation, big or small, helps!", + THANK_YOU: "Thank you for your support!" +}; - title="Your donations help us cover hosting, domains, licenses, and advertising for courses - and events. Every donation, big or small, helps!" + title={SUPPORT_MESSAGES.DESCRIPTION} - title="Thank you for your support!" + title={SUPPORT_MESSAGES.THANK_YOU}
π Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
π Files selected for processing (7)
src/shared/constants.ts
(2 hunks)src/shared/ui/logo/logo.tsx
(2 hunks)src/widgets/header/header.module.scss
(4 hunks)src/widgets/header/header.tsx
(6 hunks)src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss
(2 hunks)src/widgets/mobile-view/ui/mobile-view.tsx
(2 hunks)src/widgets/school-menu/ui/school-menu/school-menu.module.scss
(2 hunks)
π§ Files skipped from review as they are similar to previous changes (6)
- src/widgets/header/header.module.scss
- src/widgets/school-menu/ui/school-menu/school-menu.module.scss
- src/widgets/header/header.tsx
- src/shared/constants.ts
- src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss
- src/shared/ui/logo/logo.tsx
π Additional comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)
48-67
: Well-implemented state management with proper cleanup.The activeDropdowns state management using Set is efficient and the useEffect cleanup when menu closes is correctly implemented.
40-243
:β Verification inconclusive
Verify keyboard navigation functionality.
Based on PR comments, keyboard navigation may no longer work after these changes. Please test that all interactive elements are accessible via keyboard.
Test the following keyboard interactions:
- Tab navigation through all menu items
- Enter/Space to open/close dropdowns
- Arrow keys for menu navigation
- Escape to close menus
π Script executed:
#!/bin/bash # Check for ARIA attributes and accessibility patterns ast-grep --pattern 'role=$_' ast-grep --pattern 'aria-$_=$_' ast-grep --pattern 'tabIndex=$_'Length of output: 99
Verify keyboard navigation functionality
Please perform manual testing to ensure all interactive elements in the mobile menu are accessible via keyboard:
- Tab through each menu item and focusable control
- Use Enter/Space to open and close dropdowns
- Navigate within open menus using Arrow keys
- Press Escape to close any open dropdown or the menu
If any of these interactions do not behave as expected, update the component to include proper ARIA roles,
tabIndex
, and keyboard event handlers.
Lighthouse Report:
|
Lighthouse Report:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (3)
src/widgets/header/ui/nav-item/nav-item.tsx (3)
41-53
: Global event system needs documentation.The custom event approach for closing all dropdowns works but could benefit from being moved to a shared constant or utility to prevent string duplication across components.
Consider extracting the event name to a shared constant:
- const closeAllDropdowns = 'closeAllDropdowns'; + const closeAllDropdowns = CUSTOM_EVENTS.CLOSE_ALL_DROPDOWNS;
99-131
: Button structure and duplicate labels need clarification.The JSX structure is well-organized, but the duplicate label rendering seems unnecessary and could confuse screen readers despite the
aria-hidden
attribute.Consider simplifying the label structure:
- <span className={cx('label-bold')}> - {label} - <span className={cx('label-content')} aria-hidden="true" data-testid="label-content"> - {label} - </span> - </span> + <span className={cx('label-bold')}> + {label} + </span>If the duplicate is needed for styling, explain the purpose in a comment.
127-129
: Dropdown arrow ARIA attributes could be improved.The arrow span has
role="button"
but the parent button already handles the interaction. This creates confusing semantics.Remove redundant role since the parent button handles interaction:
- <span className={cx('dropdown-arrow')} role="button" aria-expanded={isDropdownOpen}> + <span className={cx('dropdown-arrow')} aria-hidden="true">The
aria-expanded
should be on the main button instead.
π Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
π Files selected for processing (10)
src/core/styles/_constants.scss
(1 hunks)src/core/styles/_mixins.scss
(1 hunks)src/widgets/breadcrumbs/ui/breadcrumbs.module.scss
(1 hunks)src/widgets/header/header.module.scss
(4 hunks)src/widgets/header/header.test.tsx
(5 hunks)src/widgets/header/ui/nav-item/nav-item.module.scss
(3 hunks)src/widgets/header/ui/nav-item/nav-item.tsx
(2 hunks)src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss
(1 hunks)src/widgets/mobile-view/ui/mobile-view.module.scss
(1 hunks)src/widgets/school-menu/ui/school-item/school-item.module.scss
(3 hunks)
π§ Files skipped from review as they are similar to previous changes (9)
- src/core/styles/_constants.scss
- src/widgets/breadcrumbs/ui/breadcrumbs.module.scss
- src/core/styles/_mixins.scss
- src/widgets/school-menu/ui/school-item/school-item.module.scss
- src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss
- src/widgets/mobile-view/ui/mobile-view.module.scss
- src/widgets/header/header.test.tsx
- src/widgets/header/header.module.scss
- src/widgets/header/ui/nav-item/nav-item.module.scss
π Additional comments (7)
src/widgets/header/ui/nav-item/nav-item.tsx (7)
4-4
: Import additions look good.The new imports support the enhanced functionality correctly -
useCallback
for memoization, Next.js components for navigation, constants for keyboard handling, and the new outside click hook.Also applies to: 10-11, 14-15
25-26
: Props addition enhances component flexibility.The optional
icon
andreverseLayout
props provide good customization options for different menu styles.
32-35
: Ref management and variable setup is solid.Good separation of concerns with separate refs for wrapper and button elements, and clear boolean logic for dropdown detection.
70-76
: Enhanced keyboard handling is excellent.The unified handler supporting Enter, Space, and Escape keys with proper event prevention significantly improves accessibility.
78-78
: Outside click hook integration is clean.Proper usage of the custom hook with conditional activation based on dropdown state.
80-86
: Event listener cleanup is properly handled.Good practice using the cleanup function in useEffect to prevent memory leaks.
55-61
:β Verification inconclusive
Navigation logic change from Link to button.
This is a significant architectural change that replaces semantic Link navigation with programmatic routing. While it works, consider the SEO and accessibility implications.
Verify that this change doesn't negatively impact:
- Search engine crawling (Links are more SEO-friendly)
- Right-click context menu functionality
- Middle-click to open in new tab behavior
π Script executed:
#!/bin/bash # Check if other navigation components still use Link elements rg -A 3 "import.*Link.*from.*next" --type tsx --type tsLength of output: 85
π Script executed:
#!/bin/bash set -e # Locate nav-item.tsx file file=$(find . -type f -name nav-item.tsx | head -n1) echo "Nav-item component path: $file" # Show context around handleClick definition and its usage on an element grep -n -C 3 "const handleClick" "$file" grep -n -C 3 "onClick" "$file" echo echo "------------------------------------------------------------------------" # Search for any Link imports across the repo rg "import.*Link.*from.*next/link" .Length of output: 1763
Ensure SEO & accessibility for nav-item navigation
Youβve switched from a semantic
<Link>
to programmaticrouter.push
, which can break:
- SEO (no crawlable
<a href>
for search engines)- Native context menu (βOpen in new tabβ)
- Middle-click/tab-opening behavior
Please verify in
src/widgets/header/ui/nav-item/nav-item.tsx
around lines 55β61 & 104β110 that non-dropdown items still render an<a href={linkHref}>
(or wrap withnext/link
) so you donβt lose these built-in behaviors.β’ If you intend to keep programmatic routing, ensure you provide a fallback
<a>
or<Link>
wrapper for link semantics.
β’ Otherwise, reconsider using<Link href={linkHref}>
with anonClick
handler for dropdowns only.
Lighthouse Report:
|
There was a problem hiding this 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
β»οΈ Duplicate comments (1)
src/widgets/mobile-view/ui/mobile-view.tsx (1)
85-240
: π οΈ Refactor suggestionExtract repeated category container pattern into reusable component.
The category container pattern is duplicated multiple times. This creates maintenance overhead and contradicts DRY principles.
Consider extracting a
MenuCategory
component:type MenuCategoryProps = { title: string; icon?: StaticImageData; color: 'light' | 'dark'; isActive: boolean; onToggle: () => void; children: React.ReactNode; }; const MenuCategory = ({ title, icon, color, isActive, onToggle, children }: MenuCategoryProps) => ( <div className={cx('category-container')}> <MobileNavItem title={title} icon={icon} color={color} isDropdownActive={isActive} onMenuItemClick={onToggle} /> <SchoolMenu isVisible={isActive}> {children} </SchoolMenu> </div> );This would significantly reduce the component size and improve maintainability.
π§Ή Nitpick comments (1)
src/widgets/mobile-view/ui/mobile-view.tsx (1)
48-61
: Optimize callback performance with useCallback.The
onMenuItemClick
function is recreated on every render, which could impact performance when the menu has many items.- const onMenuItemClick = (menuItem: string) => { - setActiveDropdowns((prev) => { - const newSet = new Set(prev); - - if (newSet.has(menuItem)) { - newSet.delete(menuItem); - } else { - newSet.add(menuItem); - } - return newSet; - }); - }; + const onMenuItemClick = useCallback((menuItem: string) => { + setActiveDropdowns((prev) => { + const newSet = new Set(prev); + + if (newSet.has(menuItem)) { + newSet.delete(menuItem); + } else { + newSet.add(menuItem); + } + return newSet; + }); + }, []);Don't forget to import
useCallback
from React.
π Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
π Files selected for processing (4)
src/shared/ui/logo/logo.tsx
(2 hunks)src/widgets/header/header.module.scss
(3 hunks)src/widgets/header/header.tsx
(3 hunks)src/widgets/mobile-view/ui/mobile-view.tsx
(2 hunks)
π§ Files skipped from review as they are similar to previous changes (3)
- src/widgets/header/header.module.scss
- src/shared/ui/logo/logo.tsx
- src/widgets/header/header.tsx
π Additional comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)
72-72
: Logo click handler correctly addresses mobile menu closure issue.Good implementation of the onClick handler for the Logo component. This addresses the issue mentioned in PR comments about the logo not closing the mobile menu.
228-228
:β Incorrect review comment
Verify performance impact of array reversal in render.
The
toReversed()
method creates a new array on every render, which could impact performance if the donate options array is large.Consider moving this operation outside the render or memoizing it:
+ const reversedDonateOptions = useMemo(() => donateOptions.toReversed(), []); - {donateOptions.toReversed().map((option) => ( + {reversedDonateOptions.map((option) => (
π Script executed:
#!/bin/bash # Check the size of donateOptions array to assess performance impact rg -A 5 -B 5 "donateOptions.*=.*\[" --type tsx --type tsLength of output: 86
π Script executed:
#!/bin/bash # Locate where donateOptions is defined or imported rg -g '*.tsx' -n "donateOptions" -A3 -B3 rg -g '*.ts' -n "donateOptions" -A3 -B3Length of output: 4471
π Script executed:
#!/bin/bash # Count number of donateOptions entries grep -c "id:" dev-data/donate-options.data.ts # Print donateOptions file contents for review sed -n '1,200p' dev-data/donate-options.data.tsLength of output: 776
No memoization needed for donateOptions β negligible cost
ThedonateOptions
array defined indev-data/donate-options.data.ts
contains only two entries. Reversing a two-item array on each render will not introduce any measurable performance overhead. You can safely leave thetoReversed().map(...)
calls as-is.Likely an incorrect or invalid review comment.
Lighthouse Report:
|
What type of PR is this? (select all that apply)
Description
Rework desktop and mobile menus according to the new design.
Related Tickets & Documents
Screenshots, Recordings
Before:
After:
Added/updated tests?
[optional] Are there any post deployment tasks we need to perform?
[optional] What gif best describes this PR or how it makes you feel?
Feel free to hit me with your best practices :))
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores