Skip to content

fix(wizard): focus next step content#2372

Draft
spike-rabbit wants to merge 1 commit into
mainfrom
fix/wizard-step-focus
Draft

fix(wizard): focus next step content#2372
spike-rabbit wants to merge 1 commit into
mainfrom
fix/wizard-step-focus

Conversation

@spike-rabbit

@spike-rabbit spike-rabbit commented Jul 17, 2026

Copy link
Copy Markdown
Member

Focus the first tabbable control after advancing a step, falling back to the next button when the step has no tabbable control.\n\nCloses #2346


Documentation.
Examples.
Dashboards Demo.
Playwright report.

Coverage Reports:

Code Coverage

Focus the first tabbable control after advancing a step, falling back to the next button when the step has no tabbable control.

Closes #2346

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements automatic focus management when navigating between wizard steps. When moving to the next step, the wizard now attempts to focus the first interactive (tabbable) element within that step, falling back to focusing the next button if no interactive elements are found. Unit tests have been added to verify this behavior. The reviewer suggested optimizing the DOM traversal in getFirstTabbableElement by using querySelectorAll('*') instead of recursive traversal.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +422 to +435
private getFirstTabbableElement(root: HTMLElement): HTMLElement | undefined {
if (this.interactivityChecker.isFocusable(root) && this.interactivityChecker.isTabbable(root)) {
return root;
}

for (const child of Array.from(root.children)) {
const tabbableChild = this.getFirstTabbableElement(child as HTMLElement);
if (tabbableChild) {
return tabbableChild;
}
}

return undefined;
}

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.

medium

Instead of recursively traversing the DOM tree and creating a new array with Array.from(root.children) at each level, you can use root.querySelectorAll('*') to retrieve all descendant elements in document order (depth-first pre-order traversal). This is more efficient, avoids recursion overhead, and simplifies the implementation.

Suggested change
private getFirstTabbableElement(root: HTMLElement): HTMLElement | undefined {
if (this.interactivityChecker.isFocusable(root) && this.interactivityChecker.isTabbable(root)) {
return root;
}
for (const child of Array.from(root.children)) {
const tabbableChild = this.getFirstTabbableElement(child as HTMLElement);
if (tabbableChild) {
return tabbableChild;
}
}
return undefined;
}
private getFirstTabbableElement(root: HTMLElement): HTMLElement | undefined {
if (this.interactivityChecker.isFocusable(root) && this.interactivityChecker.isTabbable(root)) {
return root;
}
const descendants = root.querySelectorAll<HTMLElement>('*');
for (let i = 0; i < descendants.length; i++) {
const el = descendants[i];
if (this.interactivityChecker.isFocusable(el) && this.interactivityChecker.isTabbable(el)) {
return el;
}
}
return undefined;
}

}

private focusStepContent(): void {
afterNextRender(() => {

@chintankavathia chintankavathia Jul 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

how about just using FocusTrapFactory

Suggested change
afterNextRender(() => {
const ff = this.focusFactory.create(this.currentStep?.elementRef.nativeElement)
ff.focusFirstTabbableElementWhenReady().then((focused) => {
if (!focused) {
this.nextButton()?.nativeElement?.focus();
}
ff.destroy();
});

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