Skip to content

Issues/314 enhanchment title unify header#1048

Open
sanjayahari wants to merge 13 commits intoopenwisp:masterfrom
sanjayahari:issues/314-enhanchment-title-unify-header
Open

Issues/314 enhanchment title unify header#1048
sanjayahari wants to merge 13 commits intoopenwisp:masterfrom
sanjayahari:issues/314-enhanchment-title-unify-header

Conversation

@sanjayahari
Copy link
Copy Markdown

@sanjayahari sanjayahari commented Mar 3, 2026

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #.

Please open a new issue if there isn't an existing issue yet.

Description of Changes

Please describe these changes.

Screenshot

Please include any relevant screenshots.
Screenshot 2026-03-04 004331
<img width="1792" height="993" alt="Screenshot 202
Screenshot 2026-03-04 004352
6-03-04 004343" src="https://github.com/user-attachments/assets/b7e0bfb1-30da-4794-9660-5c727daa5d6c" />

…n-pages.Unified the rendering logic for desktop and mobile navigation, logos and language switcher. Instead of maintaining two separate link-generating loops, both now utilize the renderNavLinks helper method.Added test cases. openwisp#314
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Header was refactored to expose three public render helpers: renderLogos(deviceClass), renderLanguageSwitcher(deviceClass), and renderNavLinks(deviceClass). The main render delegates to these helpers and uses a unified-header structure with device-class-aware flows. Mobile hamburger/menu handling and a mobile menu container were reworked to use the new helpers. Unit tests were added to verify active-link application, logo rendering, link and language-button counts, and mobile menu toggle behavior. Extensive responsive and structural header CSS was added. package.json Jest transformIgnorePatterns was updated to also allow transforming cheerio. needs-verify removed the user.method === "bank_card" check for the bank_card branch.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description lacks substantive detail about the changes. The 'Description of Changes' section is not filled out, only template placeholders remain. Add a detailed description of changes including: what was refactored in the header component, why the unified header structure improves the codebase, and which files were modified and why.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Issues/314 enhanchment title unify header' is partially related to the changeset. It references issue #314 and mentions 'unify header', which aligns with the main change (refactoring header component with unified rendering helpers). However, the title is awkwardly worded with a typo ('enhanchment' instead of 'enhancement') and includes the issue prefix in a way that's more suitable for commit messages than PR titles. The phrasing 'unify header' does capture the essence of the main change.

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

✨ Finishing Touches
🧪 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.

Copy link
Copy Markdown

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

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

Inline comments:
In `@client/components/header/header.js`:
- Line 1: The file header.js contains a leading BOM character before the comment
"/* eslint-disable react/no-array-index-key */"; remove the invisible Byte Order
Mark at the very start of the file so the file begins directly with the eslint
comment (no other code changes), then save/commit to ensure
bundlers/linters/diff tools no longer see the BOM.
- Around line 120-128: The internalLinks comparison is using resolved URLs while
link.url still contains the "{orgSlug}" placeholder, so
internalLinks.indexOf(link.url) will always be -1; fix the isInternal
calculation in the header component by comparing against the resolved URL (e.g.,
compute const resolvedUrl = link.url.replace("{orgSlug}", orgSlug)) and use
internalLinks.indexOf(resolvedUrl) (and for pathname equality compare pathname
to resolvedUrl) so functions/vars to update are the isInternal assignment, the
activeClass pathname check, and references to link.url in this block (helpers:
isInternalLink, shouldLinkBeShown, internalLinks).

In `@client/components/header/header.test.js`:
- Around line 279-292: Replace the deprecated Enzyme call .filterWhere with
.filter in the test: update the two lines that create loginLink and signupLink
(the wrapper.find('.header-link').filterWhere(...) expressions) to use
wrapper.find('.header-link').filter(predicate) instead, keeping the same
predicate logic (n => n.prop('to') === '/default/login' and n => n.prop('to')
=== '/default/registration') so loginLink and signupLink creation uses .filter
rather than .filterWhere.

In `@client/components/header/index.css`:
- Around line 96-98: There are two duplicate CSS rules for the selector
.header-link.active; remove the redundant one and consolidate into a single
definition (keep the more complete rule currently at the later occurrence) so
all properties for .header-link.active live in one place; update any differing
declarations by merging their properties into the remaining .header-link.active
rule and delete the earlier/duplicate block to avoid specificity/confusion.
- Line 145: Consolidate the two mobile media-query blocks into a single block by
merging the styles from the duplicate "@media screen and (min-width: 0px) and
(max-width: 767px)" and the other "@media ... (max-width: 767px)" into one
"@media (max-width: 767px)" section; remove the redundant "min-width: 0px",
ensure rules for selectors in header styles (e.g., any rules currently inside
those media queries such as .header, .nav, .mobile-menu) are combined without
duplication and that ordering/ specificity is preserved so mobile styles apply
consistently and are easier to maintain.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71c1db2 and 89e3a02.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • client/components/header/header.js
  • client/components/header/header.test.js
  • client/components/header/index.css
  • package.json
📜 Review details
🔇 Additional comments (6)
client/components/header/header.test.js (1)

294-313: New tests for unified layout rendering look good.

These tests validate the unified header behavior including logo rendering, link counts, language button counts, and mobile menu toggle functionality.

client/components/header/header.js (3)

64-94: Modular renderLogos helper looks good.

Clean extraction of logo rendering logic with proper optional chaining for conditional rendering.


96-113: Modular renderLanguageSwitcher helper looks good.

Clean implementation with appropriate class composition for device-specific styling.


148-199: Render method structure is well-organized.

The unified header layout with separate rows for logos, navigation, and mobile menu is cleanly structured with appropriate responsive class handling.

client/components/header/index.css (1)

248-291: New responsive utility classes and unified header styling look good.

The addition of .mobile-only, .desktop-only, .unified-header, and related responsive rules provides a solid foundation for the unified header layout.

package.json (1)

107-109: cross-env usage is incorrect and may be missing from dependencies.

Two issues:

  1. Missing dependency: cross-env is not declared in dependencies or devDependencies (if this is true, these scripts will fail during build).

  2. Incorrect syntax: Using && after cross-env breaks environment variable propagation. cross-env sets variables only for the command directly following it. With &&, subsequent commands (like npm run setup and webpack) will not receive the NODE_OPTIONS variable.

Correct approaches:

  • Apply cross-env only to the command that needs the variable:
-    "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider && npm run setup && webpack --mode production --progress --config config/webpack.config.js",
+    "build": "npm run setup && cross-env NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --progress --config config/webpack.config.js",
  • Or use cross-env-shell for inline shell commands with proper quoting.

If cross-env is not in devDependencies, add it:

  "devDependencies": {
+   "cross-env": "^7.0.3",

Comment thread client/components/header/header.js Outdated
Comment thread client/components/header/header.js Outdated
Comment thread client/components/header/header.test.js
Comment thread client/components/header/index.css
Comment thread client/components/header/index.css
Copy link
Copy Markdown

@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

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

Inline comments:
In `@package.json`:
- Around line 107-109: package.json scripts "build", "build-dev", and "client"
misuse cross-env (and cross-env is missing from devDependencies): add
"cross-env" to devDependencies and change each script so cross-env prefixes the
webpack command that needs NODE_OPTIONS rather than being a separate command;
for example, keep running setup first (npm run setup) then invoke
webpack/webpack serve with cross-env NODE_OPTIONS=--openssl-legacy-provider ...
so the environment variable is applied to the webpack/serve process (update
script names build, build-dev, client accordingly).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89e3a02 and 84ff98b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • package.json

Comment thread package.json Outdated
…n-pages.Unified the rendering logic for desktop and mobile navigation, logos and language switcher. Instead of maintaining two separate link-generating loops, both now utilize the renderNavLinks helper method.Added test cases.Resolved some checks openwisp#314
…n-pages.Unified the rendering logic for desktop and mobile navigation, logos and language switcher. Instead of maintaining two separate link-generating loops, both now utilize the renderNavLinks helper method.Added test cases.Resolved some checks.Resolved yarn.lock merge conflict openwisp#314
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
client/components/header/index.css (1)

52-61: ⚠️ Potential issue | 🟠 Major

Do not remove keyboard focus visibility on language buttons.

outline: none is set, but there is no replacement focus style for keyboard navigation.

Proposed fix
 .header-language-btn {
@@
-  outline: none;
   cursor: pointer;
 }
+
+.header-language-btn:focus-visible {
+  outline: 2px solid `#d23c22`;
+  outline-offset: 2px;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/components/header/index.css` around lines 52 - 61, The
.header-language-btn rule removes keyboard focus visibility; revert/remove
outline: none and add an accessible focus indicator by defining a :focus and/or
:focus-visible style for .header-language-btn (e.g., visible outline or
box-shadow with sufficient contrast) so keyboard users can see focus when
tabbing; ensure the new focus style is non-destructive to layout and meets
contrast/visibility requirements.
♻️ Duplicate comments (5)
client/components/header/index.css (2)

96-98: 🧹 Nitpick | 🔵 Trivial

Consolidate duplicate .header-link.active declarations.

This selector is defined twice; keep one merged block to avoid drift and confusion.

Also applies to: 138-143

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

In `@client/components/header/index.css` around lines 96 - 98, Duplicate CSS rule
for the selector .header-link.active appears in the stylesheet; consolidate by
keeping a single .header-link.active rule and merge any differing properties
from the two blocks into that one declaration, then remove the other duplicate
to prevent drift and confusion (ensure any styles from both occurrences are
preserved in the remaining .header-link.active rule).

145-145: 🧹 Nitpick | 🔵 Trivial

Merge overlapping mobile media-query blocks for the same breakpoint.

You currently maintain two max-width: 767px blocks; consolidate into one block for predictable ordering and simpler maintenance.

Also applies to: 266-289

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

In `@client/components/header/index.css` at line 145, There are duplicate mobile
media-query blocks using "@media screen and (min-width: 0px) and (max-width:
767px)"; locate both blocks (one starting around the existing selector at line
~145 and the other at ~266-289) and consolidate them into a single `@media` block
named the same, moving all contained rules into that single block, resolving any
conflicting declarations by preserving the intended final value (last-wins) and
keeping selector order; remove the duplicate block afterwards so there is only
one mobile media-query for that breakpoint to simplify maintenance.
client/components/header/header.js (2)

126-130: ⚠️ Potential issue | 🟠 Major

Fix internal-link comparison to use resolved URL.

internalLinks contains resolved paths, but comparison uses raw link.url with {orgSlug} placeholder, so matching is unreliable.

Proposed fix
         {links?.map((link, index) => {
           if (!shouldLinkBeShown(link, isAuthenticated, userData)) return null;
+          const resolvedUrl = link.url.replace("{orgSlug}", orgSlug);

           const isInternal =
             isInternalLink(link.url) &&
-            (internalLinks.indexOf(link.url) < 0 || !isAuthenticated);
+            (internalLinks.indexOf(resolvedUrl) < 0 || !isAuthenticated);
           const activeClass =
-            pathname === link.url.replace("{orgSlug}", orgSlug) ? "active" : "";
+            pathname === resolvedUrl ? "active" : "";
@@
-            <Link {...commonProps} to={link.url.replace("{orgSlug}", orgSlug)}>
+            <Link {...commonProps} to={resolvedUrl}>

Also applies to: 137-137

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

In `@client/components/header/header.js` around lines 126 - 130, The internal-link
check and activeClass comparison are using the raw link.url (which may contain
the "{orgSlug}" placeholder) instead of the resolved path; update the logic in
isInternal (and the activeClass computation) to first resolve link.url by
replacing "{orgSlug}" with orgSlug (e.g., resolvedUrl =
link.url.replace("{orgSlug}", orgSlug)) and then use resolvedUrl when checking
internalLinks.indexOf(...) and when comparing to pathname; reference symbols:
isInternalLink, internalLinks, link.url, orgSlug, isInternal, activeClass,
pathname.

1-1: ⚠️ Potential issue | 🟡 Minor

Remove the BOM at file start.

The leading BOM triggers lint/tooling issues and should be removed.

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

In `@client/components/header/header.js` at line 1, The file header.js contains a
leading Byte Order Mark (BOM) before the eslint-disable comment which triggers
lint/tooling issues; open header.js and re-save/remove the BOM so the first
character is the slash of "/* eslint-disable react/no-array-index-key */" (UTF-8
without BOM) ensuring no invisible characters precede that comment.
package.json (1)

108-110: ⚠️ Potential issue | 🔴 Critical

Fix cross-env usage in build scripts (currently ineffective) and declare it.

cross-env is being run as a standalone command, so NODE_OPTIONS is not applied to webpack, and cross-env is not declared in devDependencies. This can break build/dev scripts.

Proposed fix
   "devDependencies": {
+    "cross-env": "^7.0.3",
     "@babel/core": "^7.28.5",
     "@babel/eslint-parser": "^7.28.5",
     "@babel/helper-function-name": "^7.24.7",
@@
-    "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider && npm run setup && webpack --mode production --progress --config config/webpack.config.js",
-    "build-dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider && npm run setup && webpack --mode development --progress --config config/webpack.config.js",
-    "client": "cross-env NODE_OPTIONS=--openssl-legacy-provider && npm run setup && webpack serve --host 0.0.0.0 --mode development --config config/webpack.config.js",
+    "build": "npm run setup && cross-env NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --progress --config config/webpack.config.js",
+    "build-dev": "npm run setup && cross-env NODE_OPTIONS=--openssl-legacy-provider webpack --mode development --progress --config config/webpack.config.js",
+    "client": "npm run setup && cross-env NODE_OPTIONS=--openssl-legacy-provider webpack serve --host 0.0.0.0 --mode development --config config/webpack.config.js",
#!/bin/bash
set -euo pipefail

echo "Checking cross-env declaration:"
rg -n '"cross-env"' package.json

echo
echo "Checking build script forms:"
rg -n '"build"|"build-dev"|"client"' package.json
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 108 - 110, The build scripts ("build",
"build-dev", "client") misuse cross-env by running it as a standalone command so
NODE_OPTIONS is not applied to webpack; update those scripts to run the whole
chained command under cross-env (e.g., use cross-env-shell to wrap
"NODE_OPTIONS=... npm run setup && webpack ..." or ensure cross-env prefixes the
webpack invocation) and add "cross-env" to devDependencies in package.json so
it's installed; target the "build", "build-dev", "client" script entries and the
devDependencies section when applying the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/components/header/header.js`:
- Around line 81-85: The header image render currently only checks secondLogo
which can still call getAssetPath(orgSlug, secondLogo.url) with an undefined
url; update the guard to verify the URL exists (e.g., change the conditional
around the block that renders the <img> to secondLogo && secondLogo.url) so the
image and getAssetPath are only invoked when secondLogo.url is present,
referencing the header component's secondLogo usage and getAssetPath call to
locate the code.

In `@client/components/header/header.test.js`:
- Around line 287-295: The tests currently call .first().hasClass("active") on
the Enzyme wrappers loginLink and signupLink, which only checks the first match
and can miss regressions in mobile/desktop duplicates; update the assertions to
validate all matched nodes by using Enzyme's everyWhere (or an equivalent
iteration) on loginLink and signupLink so that loginLink.everyWhere(node =>
node.hasClass("active")) is true and signupLink.everyWhere(node =>
!node.hasClass("active")) is true, replacing the .first() assertions for both
cases.

In `@client/components/header/index.css`:
- Around line 122-127: The .sticky-msg CSS rule uses the deprecated property
word-wrap; replace it with overflow-wrap: break-word to satisfy stylelint and
modern CSS standards—update the .sticky-msg block to remove word-wrap and add
overflow-wrap: break-word while keeping the existing margin-left, width,
text-align, and line-height declarations.

---

Outside diff comments:
In `@client/components/header/index.css`:
- Around line 52-61: The .header-language-btn rule removes keyboard focus
visibility; revert/remove outline: none and add an accessible focus indicator by
defining a :focus and/or :focus-visible style for .header-language-btn (e.g.,
visible outline or box-shadow with sufficient contrast) so keyboard users can
see focus when tabbing; ensure the new focus style is non-destructive to layout
and meets contrast/visibility requirements.

---

Duplicate comments:
In `@client/components/header/header.js`:
- Around line 126-130: The internal-link check and activeClass comparison are
using the raw link.url (which may contain the "{orgSlug}" placeholder) instead
of the resolved path; update the logic in isInternal (and the activeClass
computation) to first resolve link.url by replacing "{orgSlug}" with orgSlug
(e.g., resolvedUrl = link.url.replace("{orgSlug}", orgSlug)) and then use
resolvedUrl when checking internalLinks.indexOf(...) and when comparing to
pathname; reference symbols: isInternalLink, internalLinks, link.url, orgSlug,
isInternal, activeClass, pathname.
- Line 1: The file header.js contains a leading Byte Order Mark (BOM) before the
eslint-disable comment which triggers lint/tooling issues; open header.js and
re-save/remove the BOM so the first character is the slash of "/* eslint-disable
react/no-array-index-key */" (UTF-8 without BOM) ensuring no invisible
characters precede that comment.

In `@client/components/header/index.css`:
- Around line 96-98: Duplicate CSS rule for the selector .header-link.active
appears in the stylesheet; consolidate by keeping a single .header-link.active
rule and merge any differing properties from the two blocks into that one
declaration, then remove the other duplicate to prevent drift and confusion
(ensure any styles from both occurrences are preserved in the remaining
.header-link.active rule).
- Line 145: There are duplicate mobile media-query blocks using "@media screen
and (min-width: 0px) and (max-width: 767px)"; locate both blocks (one starting
around the existing selector at line ~145 and the other at ~266-289) and
consolidate them into a single `@media` block named the same, moving all contained
rules into that single block, resolving any conflicting declarations by
preserving the intended final value (last-wins) and keeping selector order;
remove the duplicate block afterwards so there is only one mobile media-query
for that breakpoint to simplify maintenance.

In `@package.json`:
- Around line 108-110: The build scripts ("build", "build-dev", "client") misuse
cross-env by running it as a standalone command so NODE_OPTIONS is not applied
to webpack; update those scripts to run the whole chained command under
cross-env (e.g., use cross-env-shell to wrap "NODE_OPTIONS=... npm run setup &&
webpack ..." or ensure cross-env prefixes the webpack invocation) and add
"cross-env" to devDependencies in package.json so it's installed; target the
"build", "build-dev", "client" script entries and the devDependencies section
when applying the change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84ff98b and 0fd930d.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • client/components/header/header.js
  • client/components/header/header.test.js
  • client/components/header/index.css
  • client/utils/needs-verify.js
  • package.json
📜 Review details
🧰 Additional context used
🧬 Code graph analysis (1)
client/components/header/header.js (4)
client/utils/get-asset-path.js (1)
  • getAssetPath (1-1)
client/utils/should-link-be-shown.js (2)
  • shouldLinkBeShown (2-29)
  • userData (3-3)
client/utils/check-internal-links.js (1)
  • isInternalLink (1-4)
client/utils/get-text.js (1)
  • getText (1-5)
🪛 ESLint
client/components/header/header.js

[error] 1-1: Unexpected Unicode BOM (Byte Order Mark).

(unicode-bom)

🪛 Stylelint (17.3.0)
client/components/header/index.css

[error] 126-126: Expected "word-wrap" to be "overflow-wrap" (property-no-deprecated)

(property-no-deprecated)

🔇 Additional comments (2)
client/utils/needs-verify.js (1)

21-23: Bank-card verification gating looks correct.

The added checks for payment URL and subscriptions are appropriate for this path.

client/components/header/header.test.js (1)

310-321: Good interaction coverage for hamburger/mobile menu state transition.

This test directly validates class toggling tied to menu state and strengthens regression safety.

Comment thread client/components/header/header.js
Comment thread client/components/header/header.test.js Outdated
Comment thread client/components/header/index.css
@sanjayahari sanjayahari force-pushed the issues/314-enhanchment-title-unify-header branch from 2baa57e to 59b2ee1 Compare March 3, 2026 20:16
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (2)
client/components/header/header.js (2)

81-85: ⚠️ Potential issue | 🟡 Minor

Guard second logo rendering by URL presence.

secondLogo truthiness alone is insufficient; getAssetPath(orgSlug, secondLogo.url) still runs with an undefined URL.

Proposed fix
-        {secondLogo && (
+        {secondLogo?.url && (
           <div className="header-logo-2">
             <img
               src={getAssetPath(orgSlug, secondLogo.url)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/components/header/header.js` around lines 81 - 85, The conditional
rendering currently checks only secondLogo, but still calls
getAssetPath(orgSlug, secondLogo.url) which can run with an undefined URL;
update the guard to require a URL (e.g., check secondLogo && secondLogo.url or
use optional chaining secondLogo?.url) before rendering the <div
className="header-logo-2"> and before invoking getAssetPath, and provide a safe
alt/fallback value for secondLogo.alternate_text to avoid accessing undefined
properties.

126-130: ⚠️ Potential issue | 🟠 Major

Fix internal-link detection to compare resolved URLs.

internalLinks stores resolved paths, but the check uses templated link.url, so the condition is ineffective.

Proposed fix
-          const isInternal =
-            isInternalLink(link.url) &&
-            (internalLinks.indexOf(link.url) < 0 || !isAuthenticated);
-          const activeClass =
-            pathname === link.url.replace("{orgSlug}", orgSlug) ? "active" : "";
+          const resolvedUrl = link.url.replace("{orgSlug}", orgSlug);
+          const isInternal =
+            isInternalLink(link.url) &&
+            (internalLinks.indexOf(resolvedUrl) < 0 || !isAuthenticated);
+          const activeClass = pathname === resolvedUrl ? "active" : "";
@@
-            <Link {...commonProps} to={link.url.replace("{orgSlug}", orgSlug)}>
+            <Link {...commonProps} to={resolvedUrl}>

Also applies to: 137-137

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

In `@client/components/header/header.js` around lines 126 - 130, The internal-link
check uses the templated link.url while internalLinks contains resolved paths;
compute a resolvedUrl once (e.g. const resolvedUrl =
link.url.replace("{orgSlug}", orgSlug)) and use resolvedUrl in both the
isInternalLink/internalLinks membership check and when comparing to pathname
(replace usages of link.url in the isInternal/internalLinks.indexOf and
activeClass comparison with resolvedUrl); apply the same change to the similar
occurrence around the other check at line ~137.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@client/components/header/header.js`:
- Around line 81-85: The conditional rendering currently checks only secondLogo,
but still calls getAssetPath(orgSlug, secondLogo.url) which can run with an
undefined URL; update the guard to require a URL (e.g., check secondLogo &&
secondLogo.url or use optional chaining secondLogo?.url) before rendering the
<div className="header-logo-2"> and before invoking getAssetPath, and provide a
safe alt/fallback value for secondLogo.alternate_text to avoid accessing
undefined properties.
- Around line 126-130: The internal-link check uses the templated link.url while
internalLinks contains resolved paths; compute a resolvedUrl once (e.g. const
resolvedUrl = link.url.replace("{orgSlug}", orgSlug)) and use resolvedUrl in
both the isInternalLink/internalLinks membership check and when comparing to
pathname (replace usages of link.url in the isInternal/internalLinks.indexOf and
activeClass comparison with resolvedUrl); apply the same change to the similar
occurrence around the other check at line ~137.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd930d and dfa15ae.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • client/components/header/header.js
  • package.json
📜 Review details
⏰ 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: Tests and Coverage
🧰 Additional context used
🧬 Code graph analysis (1)
client/components/header/header.js (4)
client/utils/get-asset-path.js (1)
  • getAssetPath (1-1)
client/utils/should-link-be-shown.js (2)
  • shouldLinkBeShown (2-29)
  • userData (3-3)
client/utils/check-internal-links.js (1)
  • isInternalLink (1-4)
client/utils/get-text.js (1)
  • getText (1-5)
🔇 Additional comments (2)
package.json (1)

157-157: Jest transform allowlist update looks good.

Allowlisting cheerio here is a sensible compatibility fix for test execution with the updated header test surface.

client/components/header/header.js (1)

155-197: Good refactor: unified header composition is cleaner.

Splitting logos, language switcher, and nav rendering into dedicated helpers improves readability and reduces duplication across desktop/mobile paths.

Update snapshot files to reflect the new unified mobile and desktop HTML structure.
@sanjayahari sanjayahari force-pushed the issues/314-enhanchment-title-unify-header branch from 927e465 to c7e47b9 Compare March 3, 2026 21:00
Refactored the rendering logic for desktop and mobile navigation, logos, and the language switcher to use a single renderNavLinks helper. Added test cases and resolved merge conflicts in yarn.lock.

Closes openwisp#314
@sanjayahari sanjayahari force-pushed the issues/314-enhanchment-title-unify-header branch from 060ff2b to 937ea0f Compare March 4, 2026 19:00
Refactored the rendering logic for desktop and mobile navigation, logos, and the language switcher to use a single renderNavLinks helper. Added test cases and resolved merge conflicts in yarn.lock.

Closes openwisp#314
Refactored the rendering logic for desktop and mobile navigation, logos, and the language switcher to use a single renderNavLinks helper. Added test cases and resolved merge conflicts in yarn.lock.

Closes openwisp#314
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.

1 participant