--- tags: one-library --- # FocusZone differences between Fabric and Stardust ## Utilities ### getWindow Essentially the same implementation in both. However, Fabric has an optimization in place for IE11: ```ts // Note: Accessing "window" in IE11 is somewhat expensive, and calling "typeof window" // hits a memory leak, whereas aliasing it and calling "typeof _window" does not. // Caching the window value at the file scope lets us minimize the impact. try { _window = window; } catch (e) { /* no-op */ } ``` ### getDocument Essentially the same implementation in both. However, Fabric includes a check for SSR scenarios. ### getParent **Fabric:** ```ts /** * Gets the element which is the parent of a given element. * If `allowVirtuaParents` is `true`, this method prefers the virtual parent over * real DOM parent when present. * * @public */ export function getParent(child: HTMLElement, allowVirtualParents: boolean = true): HTMLElement | null { return child && ((allowVirtualParents && getVirtualParent(child)) || (child.parentNode && (child.parentNode as HTMLElement))); } ``` ```ts import { isVirtualElement } from './isVirtualElement'; /** * Gets the virtual parent given the child element, if it exists. * * @public */ export function getVirtualParent(child: HTMLElement): HTMLElement | undefined { let parent: HTMLElement | undefined; if (child && isVirtualElement(child)) { parent = child._virtual.parent; } return parent; } ``` **Stardust:** ```ts /** * Returns parent element of passed child element if exists * @param child - element to find parent for */ export function getParent(child: HTMLElement): HTMLElement | null { return child && child.parentElement; } ``` ### elementContains vs contains Fabric uses its own custom method to determine whether or not a parent element contains a child element: ```ts /** * Determines whether or not a parent element contains a given child element. * If `allowVirtualParents` is true, this method may return `true` if the child * has the parent in its virtual element hierarchy. * * @public */ export function elementContains(parent: HTMLElement | null, child: HTMLElement | null, allowVirtualParents: boolean = true): boolean { let isContained = false; if (parent && child) { if (allowVirtualParents) { isContained = false; while (child) { let nextParent: HTMLElement | null = getParent(child); if (nextParent === parent) { isContained = true; break; } child = nextParent; } } else if (parent.contains) { isContained = parent.contains(child); } } return isContained; } ``` Stardust on the other hand uses the `contains` function that typescript provides, with the following declaration: ```ts /** * Returns true if other is an inclusive descendant of node, and false otherwise. */ contains(other: Node | null): boolean; ``` ### Eventing Fabric uses `EventGroup` while Stardust uses native eventing. ### Keyboard events Fabric uses `ev.which` against a `KeyCodes` enum while Stardust uses the `keyboardKey` library. ### RTL RTL support is provided via a global `getRTL()` method in Fabric, which differs from the support via an `isRtl` prop in Stardust. ## Props exclusive to one library ### restoreFocusFromRoot Stardust has this prop so that, If focus is on the root element after componentDidUpdate, the component will make an attempt to restore the focus to the inner element. ### focusLast Stardust has this method that doesn't exist in Fabric's FocusZone: ```ts /** * Sets focus to the last tabbable item in the zone. * @returns True if focus could be set to an active element, false if no operation was taken. */ focusLast(): boolean { if (this._root.current) { const lastChild = this._root.current && (this._root.current.lastChild as HTMLElement | null); return this.focusElement(getPreviousElement(this._root.current, lastChild, true, true, true) as HTMLElement); } return false; } ``` It does not seem like this method is used anywhere though. ### defaultTabbableElement Stardust has a prop named `defaultTabbableElement` that does not exist in Fabric with the following description: > Function which uses root element as parameter to return the intial tabbable element. For example, when there is a chat with a bottom-up approach, it is expected that the last chat message is tabbable (active), not the first default one. ### defaultActiveElement Fabric has a prop named `defaultActiveElement` that does not exist in Stardust with the following description: > Optionally provide a selector for identifying the intial active element. ### preventDefaultWhenHandled Stardust has a `preventDefaultWhenHandled` prop to prevent default behavior that doesn't exist in Fabric. ### allowFocusRoot This boolean prop that exists in Fabric but not it Stardust determines if we allow the focus to move to the root or not and serves as a gatekeeper of some scenarios. ### shouldFocusOnMount Stardust has a prop named `shouldFocusOnMount` that does not exist in Fabric with the following description: > If a default tabbable element should be force focused on FocusZone mount. ### shouldFocusInnerElementWhenReceivedFocus Stardust has a prop named `shouldFocusInnerElementWhenReceivedFocus` that does not exist in Fabric with the following description: > if true and FocusZone's root element (container) receives focus, the focus will land either on the defaultTabbableElement (if set) or on the first tabbable element of this FocusZone. Usually a case for nested focus zones, when nested focus zone's container is a focusable element. ## Props existing in both libraries with different names These props serve the same function but are named different in the repos: | Name in Fabric | Name in Stardust | |:---------------------------------:|:----------------------:| | `onBeforeFocus` | `shouldReceiveFocus` | | `isInnerZoneKeyStroke` | `shouldEnterInnerZone` | | `doNotAllowFocusEventToPropagate` | `stopFocusPropagation` | | `x` and `y` | `left` and `top` | | `onFocusNotification` | `onFocus` | ## Functionality and implementation differences ### Page Up and Page Down support Fabric has support for `Page Up` and `Page Down` keyboard presses while Stardust does not. Related props and methods: - `moveFocusPaging` - `getHorizontalDistanceFromCenter` ### Tab handling Two differences here: 1. Fabric has a fix that Stardust lacks to handle the tab key when `domOrder` has been specifed as the `FocusZoneDirection`. 2. Stardust has a prop named `shouldResetActiveElementWhenTabFromZone` that does not exist in Fabric with the following description: > If true and TAB key is not handled by FocusZone, resets current active element to null value. For example, when roving index is not desirable and focus should always reset to the default tabbable element. ### Home and End handling Almost functionally equivalent, however, Stardust checks if the element is content editable or not before taking action. ### tryInvokeClickForFocusable Implemented method in Fabric while just a stub that's yet to be implemented in Stardust. ### Wrap functionality Fabric has functionality for wrapping focus inside the FocusZone that is missing in Stardust. Related props and methods: - `shouldWrapFocus` - `checkForNoWrap` ### shouldInputLoseFocus Almost the same implementation in both, except for the fact that Fabric checks that the element should not be `readOnly` while Stardust does not. ### setRef Stardust has this helper function to support Functional components.