Skip to content

Commit

Permalink
fix(dialog, modal, sheet): preserve focus-trap extra containers acros…
Browse files Browse the repository at this point in the history
…s internal trap updates (#11548)

**Related Issue:** #11523 

## Summary

Internal calls to `updateFocusTrap` caused user-provided
`extraContainers` to be lost. This updates `useFocusTrap` to maintain
stateful focusable containers for subsequent updates.
  • Loading branch information
jcfranco authored Feb 14, 2025
1 parent f297aa2 commit 4f9c448
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 11 deletions.
3 changes: 2 additions & 1 deletion packages/calcite-components/src/components/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ export class Dialog extends LitElement implements OpenCloseComponent {
async updateFocusTrapElements(
extraContainers?: FocusTrapOptions["extraContainers"],
): Promise<void> {
this.focusTrap.updateContainerElements(extraContainers);
this.focusTrap.setExtraContainers(extraContainers);
this.focusTrap.updateContainerElements();
}

/** When defined, provides a condition to disable focus trapping. When `true`, prevents focus trapping. */
Expand Down
3 changes: 2 additions & 1 deletion packages/calcite-components/src/components/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ export class Modal extends LitElement implements OpenCloseComponent {
async updateFocusTrapElements(
extraContainers?: FocusTrapOptions["extraContainers"],
): Promise<void> {
this.focusTrap.updateContainerElements(extraContainers);
this.focusTrap.setExtraContainers(extraContainers);
this.focusTrap.updateContainerElements();
}

// #endregion
Expand Down
3 changes: 2 additions & 1 deletion packages/calcite-components/src/components/sheet/sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ export class Sheet extends LitElement implements OpenCloseComponent {
async updateFocusTrapElements(
extraContainers?: FocusTrapOptions["extraContainers"],
): Promise<void> {
this.focusTrap.updateContainerElements(extraContainers);
this.focusTrap.setExtraContainers(extraContainers);
this.focusTrap.updateContainerElements();
}

// #endregion
Expand Down
23 changes: 15 additions & 8 deletions packages/calcite-components/src/controllers/useFocusTrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ export interface UseFocusTrap {
overrideFocusTrapEl: (el: HTMLElement) => void;

/**
* Updates focusable elements within the trap.
* Sets the extra containers to be used in the focus trap.
*
* @see https://github.com/focus-trap/focus-trap#trapupdatecontainerelements
*/
updateContainerElements: (extraContainers?: FocusTrapOptions["extraContainers"]) => void;
setExtraContainers: (extraContainers?: FocusTrapOptions["extraContainers"]) => void;

/**
* Updates focusable elements within the trap.
*/
updateContainerElements: () => void;
}

interface UseFocusTrapOptions<T extends LitElement = LitElement> {
Expand Down Expand Up @@ -100,6 +105,7 @@ export const useFocusTrap = <T extends FocusTrapComponent>(
return makeGenericController<UseFocusTrap, T>((component, controller) => {
let focusTrap: FocusTrap;
let focusTrapEl: HTMLElement;
let effectiveContainers: FocusTrapOptions["extraContainers"];
const internalFocusTrapOptions = options.focusTrapOptions;

controller.onConnected(() => {
Expand All @@ -123,11 +129,9 @@ export const useFocusTrap = <T extends FocusTrapComponent>(
...internalFocusTrapOptions,
...component.focusTrapOptions,
};
effectiveContainers ||= getEffectiveContainerElements(targetEl, component);

focusTrap = createFocusTrap(
getEffectiveContainerElements(targetEl, component),
createFocusTrapOptions(targetEl, effectiveFocusTrapOptions),
);
focusTrap = createFocusTrap(effectiveContainers, createFocusTrapOptions(targetEl, effectiveFocusTrapOptions));
}

if (
Expand All @@ -146,9 +150,12 @@ export const useFocusTrap = <T extends FocusTrapComponent>(

focusTrapEl = el;
},
updateContainerElements: (extraContainers?: FocusTrapOptions["extraContainers"]) => {
setExtraContainers: (extraContainers?: FocusTrapOptions["extraContainers"]) => {
const targetEl = focusTrapEl || component.el;
return focusTrap?.updateContainerElements(getEffectiveContainerElements(targetEl, component, extraContainers));
effectiveContainers = getEffectiveContainerElements(targetEl, component, extraContainers);
},
updateContainerElements: () => {
return focusTrap?.updateContainerElements(effectiveContainers);
},
};
});
Expand Down

0 comments on commit 4f9c448

Please sign in to comment.