Skip to content

Made the list opened if tag is selected - #223

Merged
Anton-88 merged 5 commits into
mainfrom
775-fix-taglist-always-closes
Aug 29, 2025
Merged

Made the list opened if tag is selected#223
Anton-88 merged 5 commits into
mainfrom
775-fix-taglist-always-closes

Conversation

@Anton-88

Copy link
Copy Markdown
Contributor

Made the list opened if tag is selected

@github-actions

github-actions Bot commented Aug 28, 2025

Copy link
Copy Markdown

🔍 Vulnerabilities of apostrophe-cms:test

📦 Image Reference apostrophe-cms:test
digestsha256:35e5b4834a3e94aa5bfbef1f1866b475a2fcefa17aa24e96193ac76bc2433713
vulnerabilitiescritical: 1 high: 4 medium: 0 low: 0
platformlinux/amd64
size291 MB
packages984
📦 Base Image node:23-alpine
also known as
  • 23-alpine3.22
  • 23.11-alpine
  • 23.11-alpine3.22
  • 23.11.1-alpine
  • 23.11.1-alpine3.22
digestsha256:b9d38d589853406ff0d4364f21969840c3e0397087643aef8eede40edbb6c7cd
vulnerabilitiescritical: 0 high: 0 medium: 1 low: 3
critical: 1 high: 0 medium: 0 low: 0 form-data 4.0.2 (npm)

pkg:npm/form-data@4.0.2

critical 9.4: CVE--2025--7783 Use of Insufficiently Random Values

Affected range>=4.0.0
<4.0.4
Fixed version4.0.4
CVSS Score9.4
CVSS VectorCVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N
EPSS Score0.024%
EPSS Percentile5th percentile
Description

Summary

form-data uses Math.random() to select a boundary value for multipart form-encoded data. This can lead to a security issue if an attacker:

  1. can observe other values produced by Math.random in the target application, and
  2. can control one field of a request made using form-data

Because the values of Math.random() are pseudo-random and predictable (see: https://blog.securityevaluators.com/hacking-the-javascript-lottery-80cc437e3b7f), an attacker who can observe a few sequential values can determine the state of the PRNG and predict future values, includes those used to generate form-data's boundary value. The allows the attacker to craft a value that contains a boundary value, allowing them to inject additional parameters into the request.

This is largely the same vulnerability as was recently found in undici by parrot409 -- I'm not affiliated with that researcher but want to give credit where credit is due! My PoC is largely based on their work.

Details

The culprit is this line here: https://github.com/form-data/form-data/blob/426ba9ac440f95d1998dac9a5cd8d738043b048f/lib/form_data.js#L347

An attacker who is able to predict the output of Math.random() can predict this boundary value, and craft a payload that contains the boundary value, followed by another, fully attacker-controlled field. This is roughly equivalent to any sort of improper escaping vulnerability, with the caveat that the attacker must find a way to observe other Math.random() values generated by the application to solve for the state of the PRNG. However, Math.random() is used in all sorts of places that might be visible to an attacker (including by form-data itself, if the attacker can arrange for the vulnerable application to make a request to an attacker-controlled server using form-data, such as a user-controlled webhook -- the attacker could observe the boundary values from those requests to observe the Math.random() outputs). A common example would be a x-request-id header added by the server. These sorts of headers are often used for distributed tracing, to correlate errors across the frontend and backend. Math.random() is a fine place to get these sorts of IDs (in fact, opentelemetry uses Math.random for this purpose)

PoC

PoC here: https://github.com/benweissmann/CVE-2025-7783-poc

Instructions are in that repo. It's based on the PoC from https://hackerone.com/reports/2913312 but simplified somewhat; the vulnerable application has a more direct side-channel from which to observe Math.random() values (a separate endpoint that happens to include a randomly-generated request ID).

Impact

For an application to be vulnerable, it must:

  • Use form-data to send data including user-controlled data to some other system. The attacker must be able to do something malicious by adding extra parameters (that were not intended to be user-controlled) to this request. Depending on the target system's handling of repeated parameters, the attacker might be able to overwrite values in addition to appending values (some multipart form handlers deal with repeats by overwriting values instead of representing them as an array)
  • Reveal values of Math.random(). It's easiest if the attacker can observe multiple sequential values, but more complex math could recover the PRNG state to some degree of confidence with non-sequential values.

If an application is vulnerable, this allows an attacker to make arbitrary requests to internal systems.

critical: 0 high: 1 medium: 0 low: 0 linkifyjs 4.2.0 (npm)

pkg:npm/linkifyjs@4.2.0

high 8.8: CVE--2025--8101 Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

Affected range<4.3.2
Fixed version4.3.2
CVSS Score8.8
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
EPSS Score0.065%
EPSS Percentile21st percentile
Description

Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') vulnerability in Linkify (linkifyjs) allows XSS Targeting HTML Attributes and Manipulating User-Controlled Variables.This issue affects Linkify: from 4.3.1 before 4.3.2.

critical: 0 high: 1 medium: 0 low: 0 async 1.5.2 (npm)

pkg:npm/async@1.5.2

high 7.8: CVE--2021--43138 OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities

Affected range<2.6.4
Fixed version2.6.4, 3.2.2
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.907%
EPSS Percentile75th percentile
Description

A vulnerability exists in Async through 3.2.1 (fixed in 3.2.2), which could let a malicious user obtain privileges via the mapValues() method.

critical: 0 high: 1 medium: 0 low: 0 connect-multiparty 2.2.0 (npm)

pkg:npm/connect-multiparty@2.2.0

high 7.8: CVE--2022--29623 Unrestricted Upload of File with Dangerous Type

Affected range<=2.2.0
Fixed versionNot Fixed
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.320%
EPSS Percentile54th percentile
Description

An arbitrary file upload vulnerability in the file upload module of Express Connect-Multiparty 2.2.0 allows attackers to execute arbitrary code via a crafted PDF file. NOTE: the Supplier has not verified this vulnerability report.

critical: 0 high: 1 medium: 0 low: 0 async 0.9.2 (npm)

pkg:npm/async@0.9.2

high 7.8: CVE--2021--43138 OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities

Affected range<2.6.4
Fixed version2.6.4, 3.2.2
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.907%
EPSS Percentile75th percentile
Description

A vulnerability exists in Async through 3.2.1 (fixed in 3.2.2), which could let a malicious user obtain privileges via the mapValues() method.

@coderabbitai

coderabbitai Bot commented Aug 28, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Removes sessionStorage-based persistence for expanded case-studies filter categories and replaces it with runtime, data-driven visibility in clientSideFiltering.js. Adds helpers (hasSelectedTagsInCategory, isDesktop), updateCategoriesVisibility and initializeCategoriesVisibility, a resize handler, and popstate handling; integrates visibility initialization into initClientSideFiltering and adds a change-event listener to keep aria-expanded in sync with checkbox state. Updates case-studies template to open a filter section when it is first or when that filter type has an active query. Adjusts modal panel positioning in SCSS from absolute-centered overlays to sticky, top-anchored panels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • killev
  • VitalyyP

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 775-fix-taglist-always-closes

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
website/modules/asset/ui/src/clientSideFiltering.js (4)

28-31: Align desktop detection with CSS using matchMedia.

Avoid off-by-one with >1024. Use the same breakpoint as CSS.

-const isDesktop = function () {
-  // Check if current viewport is desktop (typically > 1024px)
-  return window.innerWidth > 1024;
-};
+const isDesktop = function () {
+  // Align with CSS breakpoint
+  return window.matchMedia('(min-width: 1024px)').matches;
+};

33-61: Visibility logic is sound; consider minor cleanup.

You re-query the button twice per branch; cache it once per checkbox to reduce DOM queries (tiny win).

-  checkboxes.forEach(function (checkbox) {
+  checkboxes.forEach(function (checkbox) {
     const filterType = checkbox.id.replace('filter-toggle-', '');
     const hasSelectedTags = hasSelectedTagsInCategory(filterType);
     const isIndustryCategory = filterType === 'industry';
+    const button = document.querySelector(`label[for="${checkbox.id}"]`);
     
     // Industry category should always be open on desktop
     const shouldBeOpen = hasSelectedTags || (isIndustryCategory && isDesktop());
     
     if (shouldBeOpen && !checkbox.checked) {
       checkbox.checked = true;
-      // Update aria-expanded attribute
-      const button = document.querySelector(`label[for="${checkbox.id}"]`);
       if (button) {
         button.setAttribute('aria-expanded', 'true');
       }
     } else if (!shouldBeOpen && checkbox.checked) {
       checkbox.checked = false;
-      // Update aria-expanded attribute
-      const button = document.querySelector(`label[for="${checkbox.id}"]`);
       if (button) {
         button.setAttribute('aria-expanded', 'false');
       }
     }
   });

120-127: Avoid brittle storage writes; simplify or guard.

If you keep persistence, guard sessionStorage access; otherwise, remove this branch.

Option A (guard):

-  if (filterLink.classList.contains('clear-all-link')) {
-    sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY);
-  } else {
-    // Save current state of expanded categories before reload
-    saveExpandedCategories();
-  }
+  try {
+    if (filterLink.classList.contains('clear-all-link')) {
+      sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY);
+    } else {
+      saveExpandedCategories();
+    }
+  } catch { /* storage may be unavailable */ }

Option B (if removing persistence per earlier comment):

-  // Clear saved state if this is a "clear all" action
-  if (filterLink.classList.contains('clear-all-link')) {
-    sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY);
-  } else {
-    // Save current state of expanded categories before reload
-    saveExpandedCategories();
-  }
+  // No-op; persistence removed

147-151: Debounce resize-driven DOM work.

Resizing triggers frequent recalculations; debounce to reduce layout thrash on continuous resize.

-const handleResize = function () {
-  // Update categories visibility when viewport changes
-  updateCategoriesVisibility();
-};
+let resizeTid;
+const handleResize = function () {
+  clearTimeout(resizeTid);
+  resizeTid = setTimeout(updateCategoriesVisibility, 150);
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f77f83b and f43964d.

📒 Files selected for processing (2)
  • website/modules/asset/ui/src/clientSideFiltering.js (3 hunks)
  • website/modules/case-studies-page/views/index.html (2 hunks)
⏰ 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). (3)
  • GitHub Check: e2e-tests
  • GitHub Check: unit-tests
  • GitHub Check: security-scan
🔇 Additional comments (2)
website/modules/case-studies-page/views/index.html (1)

98-99: Server-side default-open logic aligns with PR goal.

Opening a section when it has an active filter (or is first) matches “list opened if tag is selected.” No issues.

website/modules/asset/ui/src/clientSideFiltering.js (1)

28-31: Verify breakpoint parity with styles

I couldn’t locate any media query at 1024px in the CSS/SCSS or CSS-in-JS code. Please confirm that window.innerWidth > 1024 matches your actual filter‐panel breakpoints to avoid open/close mismatches at that boundary.

Comment thread website/modules/asset/ui/src/clientSideFiltering.js Outdated
Comment thread website/modules/asset/ui/src/clientSideFiltering.js Outdated
Comment thread website/modules/asset/ui/src/clientSideFiltering.js Outdated
Comment thread website/modules/case-studies-page/views/index.html

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (3)
website/modules/asset/ui/src/clientSideFiltering.js (3)

145-149: Add delegated change handler to keep aria-expanded in sync (and persist on toggle if keeping persistence).

Without this, manual category toggles can desync aria-expanded; also enables full persistence path.

 export const initClientSideFiltering = function () {
   if (!document.querySelector('.cs_list')) {
     return;
   }
 
   restoreExpandedCategories();
 
   window.addEventListener('popstate', handlePopState);
   window.addEventListener('resize', handleResize);
   document.addEventListener('click', handleFilterClick);
+  document.addEventListener('change', function (e) {
+    const cb = e.target && e.target.closest('.filter-category__toggle');
+    if (!cb) return;
+    const button = document.querySelector(`label[for="${cb.id}"]`);
+    if (button) {
+      button.setAttribute('aria-expanded', cb.checked ? 'true' : 'false');
+    }
+    // Optional: persist manual expansions (if keeping persistence)
+    try {
+      const expanded = [...document.querySelectorAll('.filter-category__toggle:checked')]
+        .map(el => el.id.replace('filter-toggle-', ''));
+      sessionStorage.setItem(EXPANDED_CATEGORIES_KEY, JSON.stringify(expanded));
+    } catch {}
+  });
 };

3-19: Persistence is inconsistent with behavior; remove it or complete it.

You save “expanded” categories but restore only when tags are selected, so manual expansions aren’t actually restored. Either drop persistence (simpler, matches PR scope “open if tag is selected”) or implement full persistence (restore regardless of selected tags and persist on toggle). Also guard sessionStorage.setItem to avoid QuotaExceeded errors.

Option A — remove persistence now:

-const EXPANDED_CATEGORIES_KEY = 'caseStudiesExpandedCategories';
-
-const saveExpandedCategories = function () {
-  const expandedCategories = [];
-  const checkboxes = document.querySelectorAll('.filter-category__toggle');
-  checkboxes.forEach(function (checkbox) {
-    if (checkbox.checked) {
-      const filterType = checkbox.id.replace('filter-toggle-', '');
-      expandedCategories.push(filterType);
-    }
-  });
-
-  sessionStorage.setItem(
-    EXPANDED_CATEGORIES_KEY,
-    JSON.stringify(expandedCategories),
-  );
-};
+// Persistence removed: current requirement is “open if tag is selected”.

Option B — keep persistence, but guard setItem:

-  sessionStorage.setItem(
-    EXPANDED_CATEGORIES_KEY,
-    JSON.stringify(expandedCategories),
-  );
+  try {
+    sessionStorage.setItem(
+      EXPANDED_CATEGORIES_KEY,
+      JSON.stringify(expandedCategories),
+    );
+  } catch {}

56-86: Restore path doesn’t restore manual expansions; simplify or implement fully.

Current restore only opens saved categories if they also have selected tags, so manual expansions never persist.

Option A — simplify to visibility-by-selection only:

-const restoreExpandedCategories = function () {
-  try {
-    const saved = sessionStorage.getItem(EXPANDED_CATEGORIES_KEY);
-    if (!saved) {
-      updateCategoriesVisibility();
-      return;
-    }
-    
-    const expandedCategories = JSON.parse(saved);
-    expandedCategories.forEach(function (filterType) {
-      const checkbox = document.getElementById(`filter-toggle-${filterType}`);
-      if (checkbox && !checkbox.checked) {
-        if (hasSelectedTagsInCategory(filterType)) {
-          checkbox.checked = true;
-          const button = document.querySelector(
-            `label[for="filter-toggle-${filterType}"]`,
-          );
-          if (button) {
-            button.setAttribute('aria-expanded', 'true');
-          }
-        }
-      }
-    });
-    
-    updateCategoriesVisibility();
-  } catch (error) {
-    // Fallback to default visibility logic if parsing fails
-    console.warn('Failed to restore expanded categories:', error);
-    updateCategoriesVisibility();
-  }
-};
+const restoreExpandedCategories = function () {
+  updateCategoriesVisibility();
+};

Option B — honor saved manual expansions in addition to “selected” and desktop industry:

-const restoreExpandedCategories = function () {
-  try {
-    const saved = sessionStorage.getItem(EXPANDED_CATEGORIES_KEY);
-    if (!saved) {
-      updateCategoriesVisibility();
-      return;
-    }
-    
-    const expandedCategories = JSON.parse(saved);
-    expandedCategories.forEach(function (filterType) {
-      const checkbox = document.getElementById(`filter-toggle-${filterType}`);
-      if (checkbox && !checkbox.checked) {
-        if (hasSelectedTagsInCategory(filterType)) {
-          checkbox.checked = true;
-          const button = document.querySelector(
-            `label[for="filter-toggle-${filterType}"]`,
-          );
-          if (button) {
-            button.setAttribute('aria-expanded', 'true');
-          }
-        }
-      }
-    });
-    
-    updateCategoriesVisibility();
-  } catch (error) {
-    // Fallback to default visibility logic if parsing fails
-    console.warn('Failed to restore expanded categories:', error);
-    updateCategoriesVisibility();
-  }
-};
+const restoreExpandedCategories = function () {
+  try {
+    const saved = sessionStorage.getItem(EXPANDED_CATEGORIES_KEY);
+    const expanded = saved ? new Set(JSON.parse(saved)) : new Set();
+    const checkboxes = document.querySelectorAll('.filter-category__toggle');
+    checkboxes.forEach((checkbox) => {
+      const filterType = checkbox.id.replace('filter-toggle-', '');
+      const shouldBeOpen =
+        expanded.has(filterType) ||
+        hasSelectedTagsInCategory(filterType) ||
+        (filterType === 'industry' && isDesktop());
+      if (checkbox.checked !== shouldBeOpen) {
+        checkbox.checked = shouldBeOpen;
+      }
+      const button = document.querySelector(`label[for="${checkbox.id}"]`);
+      if (button) {
+        button.setAttribute('aria-expanded', shouldBeOpen ? 'true' : 'false');
+      }
+    });
+  } catch (error) {
+    console.warn('Failed to restore expanded categories:', error);
+    updateCategoriesVisibility();
+  }
+};
🧹 Nitpick comments (4)
website/modules/asset/ui/src/clientSideFiltering.js (4)

28-31: Align viewport check with CSS breakpoints via matchMedia.

innerWidth thresholds easily drift from SCSS breakpoints; use matchMedia and confirm the exact min-width used in styles.

-const isDesktop = function () {
-  return window.innerWidth > 1024;
-};
+const isDesktop = function () {
+  return window.matchMedia('(min-width: 1025px)').matches;
+};

Please verify the SCSS breakpoint value used for “desktop”.


32-54: Always sync aria-expanded, even when checked state doesn’t change.

Currently aria only updates when you flip checked; if markup ships with mismatched aria, it stays stale. Update aria every pass and avoid duplicate querySelector calls.

-const updateCategoriesVisibility = function () {
-  const checkboxes = document.querySelectorAll('.filter-category__toggle');
-  checkboxes.forEach(function (checkbox) {
-    const filterType = checkbox.id.replace('filter-toggle-', '');
-    const hasSelectedTags = hasSelectedTagsInCategory(filterType);
-    const isIndustryCategory = filterType === 'industry';
-
-    const shouldBeOpen = hasSelectedTags || (isIndustryCategory && isDesktop());
-    if (shouldBeOpen && !checkbox.checked) {
-      checkbox.checked = true;
-      const button = document.querySelector(`label[for="${checkbox.id}"]`);
-      if (button) {
-        button.setAttribute('aria-expanded', 'true');
-      }
-    } else if (!shouldBeOpen && checkbox.checked) {
-      checkbox.checked = false;
-      const button = document.querySelector(`label[for="${checkbox.id}"]`);
-      if (button) {
-        button.setAttribute('aria-expanded', 'false');
-      }
-    }
-  });
-};
+const updateCategoriesVisibility = function () {
+  const checkboxes = document.querySelectorAll('.filter-category__toggle');
+  checkboxes.forEach(function (checkbox) {
+    const filterType = checkbox.id.replace('filter-toggle-', '');
+    const shouldBeOpen =
+      hasSelectedTagsInCategory(filterType) ||
+      (filterType === 'industry' && isDesktop());
+
+    if (checkbox.checked !== shouldBeOpen) {
+      checkbox.checked = shouldBeOpen;
+    }
+    const button = document.querySelector(`label[for="${checkbox.id}"]`);
+    if (button) {
+      button.setAttribute('aria-expanded', shouldBeOpen ? 'true' : 'false');
+    }
+  });
+};

112-117: Guard storage ops and/or remove per Option A.

If keeping persistence, wrap storage calls; if removing persistence, delete this block entirely.

Option A — remove:

-  if (filterLink.classList.contains('clear-all-link')) {
-    sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY);
-  } else {
-    saveExpandedCategories();
-  }

Option B — guard:

-  if (filterLink.classList.contains('clear-all-link')) {
-    sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY);
-  } else {
-    saveExpandedCategories();
-  }
+  if (filterLink.classList.contains('clear-all-link')) {
+    try { sessionStorage.removeItem(EXPANDED_CATEGORIES_KEY); } catch {}
+  } else {
+    // saveExpandedCategories() already guards setItem in Option B above
+    saveExpandedCategories();
+  }

136-139: Throttle resize to avoid excessive DOM scans.

updateCategoriesVisibility() queries the DOM; throttle via rAF.

-const handleResize = function () {
-  updateCategoriesVisibility();
-};
+let __resizeRaf;
+const handleResize = function () {
+  if (__resizeRaf) cancelAnimationFrame(__resizeRaf);
+  __resizeRaf = requestAnimationFrame(() => {
+    updateCategoriesVisibility();
+    __resizeRaf = null;
+  });
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f43964d and a5d6454.

📒 Files selected for processing (1)
  • website/modules/asset/ui/src/clientSideFiltering.js (3 hunks)
⏰ 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). (2)
  • GitHub Check: e2e-tests
  • GitHub Check: security-scan
🔇 Additional comments (1)
website/modules/asset/ui/src/clientSideFiltering.js (1)

21-26: LGTM: active-tag detection is straightforward.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (1)
website/modules/asset/ui/src/clientSideFiltering.js (1)

3-4: Persistence removal acknowledged and correct.

Matches the PR scope “open if tag is selected”; no dead sessionStorage left.

🧹 Nitpick comments (6)
website/modules/asset/ui/src/clientSideFiltering.js (6)

5-10: Remove trailing comma for wider browser compatibility.

Trailing commas in function calls can trip older runtimes; also simplifies diff noise.

-  const selectedTags = document.querySelectorAll(
-    `#filter-content-${filterType} .tag-item.active`,
-  );
+  const selectedTags = document.querySelectorAll(
+    `#filter-content-${filterType} .tag-item.active`
+  );

Confirm that “.tag-item.active” is the authoritative active-state marker in the rendered HTML.


12-14: Align breakpoint detection with CSS using matchMedia.

Avoids off-by-one mismatches with 1024/1025 CSS breakpoints.

-const isDesktop = function () {
-  return window.innerWidth > 1024;
-};
+const isDesktop = function () {
+  return window.matchMedia('(min-width: 1025px)').matches;
+};

Please confirm your SCSS/CSS desktop breakpoint (1024 vs 1025) to ensure parity.


40-42: Tiny cleanup: inline initializer.

initializeCategoriesVisibility is a single call-through; consider inlining to reduce indirection.


86-88: Throttle resize-driven DOM work.

Prevent excessive DOM queries on rapid resizes.

-const handleResize = function () {
-  updateCategoriesVisibility();
-};
+const handleResize = function () {
+  if (resizeRafId) cancelAnimationFrame(resizeRafId);
+  resizeRafId = requestAnimationFrame(updateCategoriesVisibility);
+};

Add once at module scope:

let resizeRafId = null;

Also applies to: 98-98


95-96: Make init idempotent to avoid duplicate listeners.

Protect against double-initialization in CMS/partial reloads.

// module scope
let clientFilteringInitialized = false;

// inside initClientSideFiltering(), at the top:
if (clientFilteringInitialized) return;
clientFilteringInitialized = true;

Is initClientSideFiltering guaranteed to be called exactly once per page view?


101-111: ARIA sync handler looks good.

Delegated change listener correctly maintains aria-expanded on manual toggles.

Optionally preserve user intent across resizes by tracking manual toggles (e.g., set checkbox.dataset.manualOpen = 'true'/'false' here and have updateCategoriesVisibility skip overriding when this dataset is present). I can draft that follow-up if desired.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a5d6454 and 9f58801.

📒 Files selected for processing (1)
  • website/modules/asset/ui/src/clientSideFiltering.js (2 hunks)
⏰ 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). (4)
  • GitHub Check: e2e-tests
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: security-scan
🔇 Additional comments (1)
website/modules/asset/ui/src/clientSideFiltering.js (1)

16-38: Ensure templates include data-default-open on default categories
Add the data-default-open attribute to the intended default-open section in your HTML (e.g. in website/modules/case-studies-page/views/index.html around line 120) so the JS can detect and apply desktop defaults correctly.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 28, 2025
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 28, 2025
@Anton-88
Anton-88 requested a review from VitalyyP August 28, 2025 09:40
@Anton-88 Anton-88 self-assigned this Aug 28, 2025
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
website/modules/asset/ui/src/scss/_cases.scss (1)

1289-1296: Consistency with other sticky elements

Other sticky elements in this file use $mobile-header-height/$desktop-header-height. Aligning this modal’s offset with those variables avoids header overlap and keeps behavior consistent.

If you keep a constant offset, at least document why 90px is needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5041bd8 and f5eca5a.

📒 Files selected for processing (1)
  • website/modules/asset/ui/src/scss/_cases.scss (1 hunks)
⏰ 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). (4)
  • GitHub Check: e2e-tests
  • GitHub Check: unit-tests
  • GitHub Check: security-scan
  • GitHub Check: lint
🔇 Additional comments (1)
website/modules/asset/ui/src/scss/_cases.scss (1)

1289-1296: Avoid horizontal overflow: left/right + 90vw + min-width can exceed 100vw

With left: 20px; right: 20px; width: 90vw; min-width: 300px, the rendered width can exceed the viewport on 320px devices (e.g., 300px + 40px > 320px). Rely on left/right constraints (or a clamp) instead.

Use one of:

  • Keep left/right and remove both width and min-width (as in the diff above).
  • Or, if you prefer explicit width: width: min(400px, calc(100vw - 40px)); and drop min-width.
    Please verify on iPhone SE/Small Android widths and with long filter content.

Comment thread website/modules/asset/ui/src/scss/_cases.scss
@Anton-88
Anton-88 merged commit 9b1325a into main Aug 29, 2025
12 checks passed
@Anton-88
Anton-88 deleted the 775-fix-taglist-always-closes branch August 29, 2025 12:04
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.

2 participants