Skip to content

fix(core): guard null anchor role in public menu - #65706

Open
bulungasdeveloper wants to merge 1 commit into
nextcloud:masterfrom
bulungasdeveloper:fix-publicpagemenu-null-role
Open

bulungasdeveloper wants to merge 1 commit into
nextcloud:masterfrom
bulungasdeveloper:fix-publicpagemenu-null-role

Conversation

@bulungasdeveloper

Copy link
Copy Markdown
Contributor

Summary

PublicPageMenuEntry assumes the anchor rendered by NcListItem is
already in the DOM when it mounts. If the entry mounts first,
document.getElementById() returns null and anchor.role = 'menuitem'
throws a TypeError, aborting the mount and leaving the public page menu
without entries.

Return early when the anchor is missing. Nothing changes when it exists.

How to test

  1. Open a public share link (/s/<token>).
  2. Open the browser console (preserve log) and reload.
  3. No TypeError ... setting 'role'; the menu keeps its entries and the
    anchors have role="menuitem".

The unit test mounts the component with the anchor missing and asserts
that it does not throw.

Checklist

  • Code is properly formatted (eslint)
  • Sign-off message is added to all commits
  • Tests are included (PublicPageMenuExternalEntry.spec.ts, 3/3)
  • Screenshots before/after: not applicable, no visual change
  • Documentation not required
  • Backports requested where applicable: stable35, stable34, stable33
  • Labels added: needs triage
  • Milestone added: for maintainers

AI

  • The content of this PR was partly generated using AI

@bulungasdeveloper
bulungasdeveloper requested a review from a team as a code owner October 19, 2026 23:07
@bulungasdeveloper

bulungasdeveloper commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks! The PR is now rebased on current master and the change itself is green:
eslint, NPM lint, DCO, reuse-compliance-check, test (including
PublicPageMenuExternalEntry.spec.ts) and gate pass.

The remaining red checks are environmental:

  • Playwright setup failed after 10 minutes with no steps executed
    (steps: []), so playwright-tests was skipped. merge-reports then ran
    with empty package-manager-version outputs and failed on npm i -g 'npm@'
    (EACCES), and playwright-test-summary failed because the tests were skipped.
    These are cascading from the setup failure. A full re-run (all jobs) is the
    reliable option, since the test shards were skipped rather than failed.
  • check-ai-trailers: the job tries to add the AI assisted label through the
    labels API, but pull requests from a fork get a read-only GITHUB_TOKEN
    (403 Resource not accessible by integration). The commit carries the
    required Assisted-by trailer and the label is already applied.

Could someone with write access re-run the Playwright workflow (all jobs)?

@bulungasdeveloper

Copy link
Copy Markdown
Contributor Author

Friendly ping for review.

All required checks are green (test, node, eslint, php-lint-summary,
php-cs, playwright-test-summary, static-code-analysis-summary, etc.).
The only red check is check-ai-trailers, which is not required: it fails
because a fork token cannot add the AI assisted label, and that label is
already applied. The commit also carries the Assisted-by trailer.

The PR is waiting for two approvals. Could a frontend reviewer take a look when
convenient? The branch is behind master but still mergeable; I can rebase if
you prefer.

Thanks!

Comment on lines +45 to +48
const anchor = document.getElementById(`${props.id}--link`) as HTMLAnchorElement
const anchor = document.getElementById(`${props.id}--link`) as HTMLAnchorElement | null
if (!anchor) {
return
}

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.

but this then must be retried later if this is a race condition, even better to delay mounting until its available.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback. You’re right – returning early only hides the race. I’ve updated the component to retry setting the role until the anchor is actually in the DOM.
PublicPageMenuEntry.vue now uses a small requestAnimationFrame retry loop in onMounted:

  • it tries to find document.getElementById(${id}--link)
  • if it’s missing, it schedules another attempt, up to 10 frames
  • once the anchor appears, role="menuitem" and the optional clickOnly handler are applied
    This avoids the TypeError: Cannot set properties of null without bailing out, and it still does nothing if the anchor never appears. The test remains green and the change is backward compatible.
    Commit 3247050 pushed to fix-publicpagemenu-null-role.

@bulungasdeveloper

bulungasdeveloper commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Rebased onto latest master (fe381b4) so the checks run against a current base.

All CI checks are green except check-ai-trailers. That job fails because it tries to create and apply the AI assisted label from a fork and gets Resource not accessible by integration (HTTP 403). The label is already applied manually.

The retry logic for the anchor was added as suggested: onMounted retries with requestAnimationFrame until the anchor exists instead of returning early (1cba55591d).

@susnux could you take another look when you have a moment?

The anchor rendered by NcListItem may not be in the DOM at onMounted
time, causing a race. Retry with requestAnimationFrame for up to 10
frames instead of bailing out immediately.

Assisted-by: opencode:opencode
Signed-off-by: Braux Martinez <martinezjb@hitss.com>
@bulungasdeveloper
bulungasdeveloper force-pushed the fix-publicpagemenu-null-role branch from 3247050 to 1cba555 Compare October 25, 2026 16:41

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants