Skip to content

fix: improve collapsible section state detection#1381

Merged
danielroe merged 1 commit intonpmx-dev:mainfrom
alexdln:fix/collapsible-sections-state
Feb 11, 2026
Merged

fix: improve collapsible section state detection#1381
danielroe merged 1 commit intonpmx-dev:mainfrom
alexdln:fix/collapsible-sections-state

Conversation

@alexdln
Copy link
Copy Markdown
Member

@alexdln alexdln commented Feb 11, 2026

Collapsible sections (in the sidebar of package pages) sometimes showed content that weren't clickable.

The problem was that the logic simply checked for the presence of the current section's id in the dataset. For example - if optional-dependencies were collapsed, this check also worked for dependencies section, marking the wrong section as !isOpen.

At the same time, the content was visible, since the required style for hiding was not added (it is added by passing through exact ids)

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 11, 2026 0:40am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 11, 2026 0:40am
npmx-lunaria Ignored Ignored Feb 11, 2026 0:40am

Request Review

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

The CollapsibleSection.vue component has been updated to change how it parses the collapsed elements list from localStorage. The modification replaces direct string inclusion checks with a split-based approach, applying split(' ') followed by includes checks. This change affects both the prehydration loop and the mounted lifecycle hook, altering how the collapsed string is constructed and the initial isOpen state is determined. The overall functionality remains unaffected by this parsing method adjustment.

Suggested reviewers

  • graphieros
  • 43081j
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the bug (substring matching causing wrong sections to be marked as collapsed) and how the fix addresses it (using split and exact matching).

✏️ 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

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
app/components/CollapsibleSection.vue (1)

29-35: ⚠️ Potential issue | 🟠 Major

Fix optional chaining to avoid undefined.includes crashes.

When document.documentElement.dataset.collapsed is unset, ?.split(' ') returns undefined and .includes(...) throws. This can break hydration and sidebar behaviour on first load. Use a default empty string and split safely in both locations.

Proposed fix
 onPrehydrate(() => {
   const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
   const collapsed: string[] = settings?.sidebar?.collapsed || []
   for (const id of collapsed) {
-    if (!document.documentElement.dataset.collapsed?.split(' ').includes(id)) {
-      document.documentElement.dataset.collapsed = (
-        document.documentElement.dataset.collapsed +
-        ' ' +
-        id
-      ).trim()
-    }
+    const collapsedIds = (document.documentElement.dataset.collapsed ?? '')
+      .split(' ')
+      .filter(Boolean)
+    if (!collapsedIds.includes(id)) {
+      document.documentElement.dataset.collapsed = [...collapsedIds, id].join(' ')
+    }
   }
 })
 
 onMounted(() => {
   if (document?.documentElement) {
-    isOpen.value = !(
-      document.documentElement.dataset.collapsed?.split(' ').includes(props.id) ?? false
-    )
+    const collapsedIds = (document.documentElement.dataset.collapsed ?? '')
+      .split(' ')
+      .filter(Boolean)
+    isOpen.value = !collapsedIds.includes(props.id)
   }
 })

Also applies to: 41-43

@alexdln
Copy link
Copy Markdown
Member Author

alexdln commented Feb 11, 2026

?.split(' ') returns undefined

Coderabbit definitely knows Vue a little better than JS

@danielroe danielroe added this pull request to the merge queue Feb 11, 2026
Merged via the queue into npmx-dev:main with commit ac9e023 Feb 11, 2026
17 checks passed
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