Skip to content

Commit

Permalink
Shadow DOM: FocusZone and FocusTrapZone (microsoft#30206)
Browse files Browse the repository at this point in the history
Co-authored-by: KHMakoto <Humberto.Morimoto@microsoft.com>
  • Loading branch information
spmonahan and khmakoto committed Feb 27, 2024
1 parent 4cda2ac commit 3a804d8
Show file tree
Hide file tree
Showing 42 changed files with 1,322 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add shadow DOM support for DOM APIs",
"packageName": "@fluentui/dom-utilities",
"email": "seanmonahan@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add shadow DOM support when traversing the DOM",
"packageName": "@fluentui/react-focus",
"email": "seanmonahan@microsoft.com",
"dependentChangeType": "patch"
}
6 changes: 6 additions & 0 deletions packages/dom-utilities/etc/dom-utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ export function elementContainsAttribute(element: HTMLElement, attribute: string
// @public
export function findElementRecursive(element: HTMLElement | null, matchFunction: (element: HTMLElement) => boolean, doc?: Document): HTMLElement | null;

// @public (undocumented)
export const getActiveElement: (doc: Document) => Element | null;

// @public
export function getChildren(parent: HTMLElement, allowVirtualChildren?: boolean): HTMLElement[];

// @public (undocumented)
export const getEventTarget: (event: Event) => HTMLElement | null;

// @public
export function getParent(child: HTMLElement, allowVirtualParents?: boolean): HTMLElement | null;

Expand Down
9 changes: 9 additions & 0 deletions packages/dom-utilities/src/getActiveElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const getActiveElement = (doc: Document): Element | null => {
let ae = doc.activeElement;

while (ae?.shadowRoot) {
ae = ae.shadowRoot.activeElement;
}

return ae;
};
8 changes: 8 additions & 0 deletions packages/dom-utilities/src/getEventTarget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const getEventTarget = (event: Event): HTMLElement | null => {
let target = event.target as HTMLElement;
if (target && target.shadowRoot) {
target = event.composedPath()[0] as HTMLElement;
}

return target;
};
28 changes: 24 additions & 4 deletions packages/dom-utilities/src/getParent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,28 @@ import { getVirtualParent } from './getVirtualParent';
* @public
*/
export function getParent(child: HTMLElement, allowVirtualParents: boolean = true): HTMLElement | null {
return (
child &&
((allowVirtualParents && getVirtualParent(child)) || (child.parentNode && (child.parentNode as HTMLElement)))
);
if (!child) {
return null;
}

const parent = allowVirtualParents && getVirtualParent(child);

if (parent) {
return parent;
}

// Support looking for parents in shadow DOM
if (
typeof (child as HTMLSlotElement).assignedElements !== 'function' &&
(child as HTMLElement).assignedSlot?.parentNode
) {
// Element is slotted
return (child as HTMLElement).assignedSlot as HTMLElement;
} else if (child.parentNode?.nodeType === 11) {
// nodeType 11 is DOCUMENT_FRAGMENT
// Element is in shadow root
return (child.parentNode as ShadowRoot).host as HTMLElement;
} else {
return child.parentNode as HTMLElement;
}
}
24 changes: 13 additions & 11 deletions packages/dom-utilities/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
export * from './IVirtualElement';
export * from './elementContains';
export * from './elementContainsAttribute';
export * from './findElementRecursive';
export * from './getChildren';
export * from './getParent';
export * from './getVirtualParent';
export * from './isVirtualElement';
export * from './portalContainsElement';
export * from './setPortalAttribute';
export * from './setVirtualParent';
export type { IVirtualElement } from './IVirtualElement';
export { elementContains } from './elementContains';
export { elementContainsAttribute } from './elementContainsAttribute';
export { findElementRecursive } from './findElementRecursive';
export { getActiveElement } from './getActiveElement';
export { getChildren } from './getChildren';
export { getEventTarget } from './getEventTarget';
export { getParent } from './getParent';
export { getVirtualParent } from './getVirtualParent';
export { isVirtualElement } from './isVirtualElement';
export { portalContainsElement } from './portalContainsElement';
export { DATA_PORTAL_ATTRIBUTE, setPortalAttribute } from './setPortalAttribute';
export { setVirtualParent } from './setVirtualParent';

import './version';
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ exports[`Donut chart interactions Should hide callout on mouse leave 1`] = `
opacity: 0.8;
}
>
</div>
</div>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ exports[`HorizontalBarChartWithAxis snapShot testing renders HorizontalBarChartW
x={40}
y={4}
/>
</g>
</svg>
</div>
Expand Down Expand Up @@ -746,9 +746,9 @@ exports[`HorizontalBarChartWithAxis snapShot testing renders hideLegend correctl
x={40}
y={4}
/>
</g>
</svg>
</div>
Expand Down Expand Up @@ -876,9 +876,9 @@ exports[`HorizontalBarChartWithAxis snapShot testing renders showToolTipForYAxis
x={40}
y={14.25}
/>
</g>
</svg>
</div>
Expand Down Expand Up @@ -1485,9 +1485,9 @@ exports[`HorizontalBarChartWithAxis snapShot testing renders showYAxisLables cor
x={40}
y={14.25}
/>
</g>
</svg>
</div>
Expand Down
Loading

0 comments on commit 3a804d8

Please sign in to comment.