Skip to content

Workaround for programmatically setting focus on multi-select#4162

Open
Magnusrm wants to merge 1 commit intomainfrom
fix/multiselect-not-opening-when-receiving-focus
Open

Workaround for programmatically setting focus on multi-select#4162
Magnusrm wants to merge 1 commit intomainfrom
fix/multiselect-not-opening-when-receiving-focus

Conversation

@Magnusrm
Copy link
Copy Markdown
Contributor

@Magnusrm Magnusrm commented May 4, 2026

Description

Related Issue(s)

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 focus event handling in the Multiple Select component. The input field now correctly prevents duplicate focus events and manages focus-related side effects. Users will experience more predictable and reliable behavior when interacting with the component, with smoother focus transitions and more consistent interaction patterns.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

MultipleSelectComponent adds focus state management through a useRef-based flag that prevents reacting to self-dispatched focus events. An async onFocus handler waits for a custom u-combobox element to be defined, then conditionally dispatches a focusin event after a delay to trigger combobox behavior.

Changes

Focus Event Handling in MultipleSelect

Layer / File(s) Summary
Import & State
src/layout/MultipleSelect/MultipleSelectComponent.tsx
useRef is imported; isPatchingFocus ref is initialized to track whether a focus event is self-dispatched.
Focus Handler Implementation
src/layout/MultipleSelect/MultipleSelectComponent.tsx
Suggestion.Input gains an async onFocus handler that early-exits if isPatchingFocus is set, waits for the u-combobox custom element, pauses 150ms, verifies the input remains active, sets the flag, and dispatches a focusin event to activate combobox behavior.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Workaround for programmatically setting focus on multi-select' directly aligns with the main change: adding focus-handling logic to the MultipleSelectComponent.
Description check ✅ Passed The description is mostly complete with verified checkboxes for manual testing, accessibility, documentation, Studio support, and sprint board tracking. However, it lacks a detailed explanation of what the workaround does and how it fixes the issue.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/multiselect-not-opening-when-receiving-focus

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@Magnusrm Magnusrm added kind/bug Something isn't working backport-ignore This PR is a new feature and should not be cherry-picked onto release branches labels May 4, 2026
@Magnusrm Magnusrm marked this pull request as ready for review May 4, 2026 10:22
@Magnusrm Magnusrm moved this to 🔎 In review in Team Altinn Studio May 4, 2026
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.

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 `@src/layout/MultipleSelect/MultipleSelectComponent.tsx`:
- Line 39: The delayed synthetic focusin handler can replay from an earlier
focus cycle and cause duplicate wake-ups; to fix, attach a per-focus-cycle token
(e.g., a numeric counter or timestamp stored in a ref alongside isPatchingFocus)
that you increment on each focus start (or when scheduling the delayed callback)
and capture into the scheduled callback closure; before dispatching the
synthetic focusin (the code that currently uses document.activeElement check),
compare the captured token against the current ref and bail out if they differ,
and ensure you update/reset the token on blur so stale timers cannot fire for a
new focus cycle.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dee9c046-9ad7-4e75-aaf2-3177d05cf7e2

📥 Commits

Reviewing files that changed from the base of the PR and between ffced86 and c25488d.

📒 Files selected for processing (1)
  • src/layout/MultipleSelect/MultipleSelectComponent.tsx

} = useGetOptions(baseComponentId, 'multi');
const groupBinding = useSaveValueToGroup(dataModelBindings);
const selectedValues = groupBinding.enabled ? groupBinding.selectedValues : selectedFromSimpleBinding;
const isPatchingFocus = useRef(false);
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Invalidate stale focusin replays across focus cycles.

Line 165 schedules a delayed synthetic focusin, but that callback is not tied to the focus event that created it. If the field blurs and regains focus within 150 ms, the older callback can still pass the document.activeElement check and fire during the newer focus cycle, producing duplicate wake-ups.

♻️ Suggested guard against stale callbacks
-  const isPatchingFocus = useRef(false);
+  const isPatchingFocus = useRef(false);
+  const focusPatchSequence = useRef(0);
...
               onFocus={async (e) => {
                 // Workaround for when programmatically focused by repeating group focus management

                 // If this event was triggered by our code below, reset the flag and exit.
                 if (isPatchingFocus.current) {
                   isPatchingFocus.current = false;
                   return;
                 }

-                const input = e.target;
+                const input = e.currentTarget;
+                const sequence = ++focusPatchSequence.current;

                 // Wait for the combobox to be fully defined
                 await customElements.whenDefined('u-combobox');

                 setTimeout(() => {
+                  if (focusPatchSequence.current !== sequence) {
+                    return;
+                  }
+
                   // Ensure we are still the active element
                   if (document.activeElement !== input) {
                     return;
                   }

                   // Tell the next execution of onFocus to ignore the event we are about to fire
                   isPatchingFocus.current = true;

Also applies to: 151-176

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

In `@src/layout/MultipleSelect/MultipleSelectComponent.tsx` at line 39, The
delayed synthetic focusin handler can replay from an earlier focus cycle and
cause duplicate wake-ups; to fix, attach a per-focus-cycle token (e.g., a
numeric counter or timestamp stored in a ref alongside isPatchingFocus) that you
increment on each focus start (or when scheduling the delayed callback) and
capture into the scheduled callback closure; before dispatching the synthetic
focusin (the code that currently uses document.activeElement check), compare the
captured token against the current ref and bail out if they differ, and ensure
you update/reset the token on blur so stale timers cannot fire for a new focus
cycle.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 4, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
11.8% Coverage on New Code (required ≥ 45%)
0.0% Condition Coverage on New Code (required ≥ 45%)

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Contributor

@JamalAlabdullah JamalAlabdullah left a comment

Choose a reason for hiding this comment

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

Looks good and works 👍 , but maybe needs to check coderabbitai feedback and add test for it

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

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches kind/bug Something isn't working

Projects

Status: 🔎 In review

Development

Successfully merging this pull request may close these issues.

Multi select not opening when focused by validation error link

2 participants