Skip to content

fix: avoid crash when links to components change in dynamic texts - #4386

Merged
phlipsterit merged 2 commits into
mainfrom
fix/link-to-component-changing-target
Oct 22, 2026
Merged

phlipsterit merged 2 commits into
mainfrom
fix/link-to-component-changing-target

Conversation

@phlipsterit

@phlipsterit phlipsterit commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

A component crashed with useIsHidden doesn't support changing the baseComponentId, that would break the rule of hooks when its text had a list of linkToComponent links and a link was removed from the middle of the list. A service owner hit this with an Alert listing the rows in a table that need fixing: when one row was fixed, the Alert crashed until the page was refreshed.

Summary

Links in texts are parsed from HTML and keyed by their position. When a link in the middle is removed, React reuses the LinkToPotentialNode at that position for the next link, which has another target. useIsHidden throws when its component id changes.

 <ul>
   <li key=0> <LinkToPotentialNode to="?focusComponentId=row1">
-  <li key=1> <LinkToPotentialNode to="?focusComponentId=row2">   ← row 2 removed
+  <li key=1> <LinkToPotentialNode to="?focusComponentId=row3">   ← same component, new target → useIsHidden throws

LinkToPotentialNode now reads the target and renders the existing logic in an inner component keyed on the target, so a new target mounts a new component:

<LinkToPotentialNode>                         reads focusComponentId from `to`
  <LinkToPotentialNodeInner key={componentId}>  useExternalItem, useIsHidden, renders <Link> or text

Evidence

Manual test in localtest with a copy of ttd/component-library and a page with three inputs and an Alert listing a linkToComponent link for each input with the value x. This branch was loaded through localtest's frontendVersion cookie. Fill all three, then clear the middle one:

All rows filled:

All three rows filled, the Alert lists links to Rad 1, Rad 2 and Rad 3

Before (main), middle row cleared: the Alert crashes.

Middle row cleared on main, the Alert shows the useIsHidden error

After (this branch), middle row cleared: the Alert lists Rad 1 and Rad 3.

Middle row cleared on this branch, the Alert lists links to Rad 1 and Rad 3

After the fix, the remaining "Rad 3" link still moves focus to the right input.

LinkToPotentialNode.test.tsx:

render links [paragraph1, paragraph2, paragraph3]
rerender links [paragraph1, paragraph3]
expect links "Paragraph 1", "Paragraph 3", and "Paragraph 3" points to paragraph3
  • Before: fails with the useIsHidden error.
  • After: passes. There is also a test that a single link to an existing component renders as a link.

Merge Danger

Door: two-way

Blast Radius: small

Only LinkToPotentialNode changes. Its output is the same; it now remounts when its target changes instead of reusing the old component.

useIsHidden still gets the full focusComponentId, so for targets inside a repeating group (e.g. comp-0) the hidden expressions are not found. That is a separate, existing bug and is not changed here. v9 in src/App/frontend in altinn-studio has the same LinkToPotentialNode and needs the same fix.

Related Issue(s)

  • No issue, reported by a service owner directly.

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* and backport* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where removing a middle link from a list could cause remaining links to display incorrectly or trigger an error.
    • Improved link handling when target component IDs change, preserving existing link, warning, and fallback behavior.
  • Tests

    • Added coverage for link targets and removing links from rendered lists.

Links made with linkToComponent are parsed from text without stable keys, so
React reused a LinkToPotentialNode for a link with another target when a link
was removed from the middle of a list. useIsHidden throws when its component id
changes. Key the inner component on the target so a new target mounts fresh.
@phlipsterit phlipsterit added kind/bug Something isn't working backport This PR should be cherry-picked onto older release branches squad/utforming Issues that belongs to the named squad. labels Oct 21, 2026
@phlipsterit phlipsterit moved this to 🔎 In review in Team Altinn Studio Oct 21, 2026
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 151d5802-7065-4acc-be6d-2a76753a81c0
📥 Commits

Reviewing files that changed from the base of the PR and between ecba09c and 198b967.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f6c0f66c-62f1-4abc-97d9-fff3a97f8b18
📥 Commits

Reviewing files that changed from the base of the PR and between 52e4099 and ecba09c.

📒 Files selected for processing: 2
  • src/components/form/LinkToPotentialNode.test.tsx
  • src/components/form/LinkToPotentialNode.tsx

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

LinkToPotentialNode now keys its inner implementation by target component ID. Tests cover links to existing components and removing a middle link from a rendered list.

Changes: Potential node link rendering

Layer / File(s) Summary
Keyed link rendering and list coverage
src/components/form/LinkToPotentialNode.tsx, src/components/form/LinkToPotentialNode.test.tsx
The component passes the target ID to a keyed inner implementation. Tests cover links to existing components and stable rendering after a middle link is removed.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: mikaelrss

Merge Risk: ⚪ Minimal · up to ecba0

The link update and its regression test cover the reported failure. No issue requiring resolution before merge was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: preventing crashes when links to components change in dynamic text.
Description check Passed The description is detailed and covers the cause, implementation, scope, manual validation, automated tests, and required QA sections. It states that no related issue exists and leaves sprint-board ch…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed and covers the cause, implementation, scope, manual validation, automated tests, and required QA sections. It states that no related issue exists and leaves sprint-board checkboxes unresolved, but the required change information is otherwise complete.

✨ Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@olemartinorg

Copy link
Copy Markdown
Contributor

Looks good! Fixes for the cypress failures got merged into main now

@phlipsterit
phlipsterit merged commit a866707 into main Oct 22, 2026
14 checks passed
@phlipsterit
phlipsterit deleted the fix/link-to-component-changing-target branch October 22, 2026 09:50
@github-project-automation github-project-automation Bot moved this from 🔎 In review to ✅ Done in Team Altinn Studio Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Automatic backport successful!

A backport PR has been automatically created for the release/v4.35 release branch.

The release branch release/v4.35 already existed and was updated.

The cherry-pick was clean with no conflicts. Please review the backport PR when it appears.

@phlipsterit

Copy link
Copy Markdown
Contributor Author

/publish

phlipsterit added a commit that referenced this pull request Oct 22, 2026
)

Links made with linkToComponent are parsed from text without stable keys, so
React reused a LinkToPotentialNode for a link with another target when a link
was removed from the middle of a list. useIsHidden throws when its component id
changes. Key the inner component on the target so a new target mounts fresh.
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

PR release:

  • <link rel="stylesheet" type="text/css" href="https://altinncdn.no/toolkits/altinn-app-frontend/4.35.0-pr.6014.link-to-component-changing-target.198b9671/altinn-app-frontend.css">
  • <script src="https://altinncdn.no/toolkits/altinn-app-frontend/4.35.0-pr.6014.link-to-component-changing-target.198b9671/altinn-app-frontend.js"></script>

⚙️ Building...
✅ Done!

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

Labels

backport This PR should be cherry-picked onto older release branches kind/bug Something isn't working squad/utforming Issues that belongs to the named squad.

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants