diff --git a/packages/calcite-components/src/components/action/action.tsx b/packages/calcite-components/src/components/action/action.tsx index e1befb94e76..158e755eca0 100644 --- a/packages/calcite-components/src/components/action/action.tsx +++ b/packages/calcite-components/src/components/action/action.tsx @@ -20,7 +20,7 @@ import { updateHostInteraction } from "../../utils/interactive"; import { - componentLoaded, + componentFocusable, LoadableComponent, setComponentLoaded, setUpLoadableComponent @@ -205,8 +205,7 @@ export class Action /** Sets focus on the component. */ @Method() async setFocus(): Promise { - await componentLoaded(this); - + await componentFocusable(this); this.buttonEl?.focus(); } diff --git a/packages/calcite-components/src/utils/loadable.ts b/packages/calcite-components/src/utils/loadable.ts index 2345b90cda1..b8e123f1309 100644 --- a/packages/calcite-components/src/utils/loadable.ts +++ b/packages/calcite-components/src/utils/loadable.ts @@ -1,3 +1,5 @@ +import { Build, forceUpdate } from "@stencil/core"; + /** * This helper adds support for knowing when a component has been loaded. * @@ -31,7 +33,7 @@ * // * // -------------------------------------------------------------------------- * - * async setFocus(): Promise { + * async myMethod(): Promise { * await componentLoaded(this); * } * ``` @@ -93,14 +95,14 @@ export function setComponentLoaded(component: LoadableComponent): void { } /** - * This helper util can be used to ensure a component has been loaded (The "componentDidLoad" stencil lifecycle method has been called). + * This helper util can be used to ensure a component has been loaded (The "componentDidLoad" Stencil lifecycle method has been called). * - * Requires "setUpLoadableComponent" and "setComponentLoaded" to be called first. + * Requires requires `LoadableComponent` to be implemented. * * A component developer can await this method before proceeding with any logic that requires a component to be loaded first. * * ``` - * async setFocus(): Promise { + * async myMethod(): Promise { * await componentLoaded(this); * } * ``` @@ -111,3 +113,31 @@ export function setComponentLoaded(component: LoadableComponent): void { export function componentLoaded(component: LoadableComponent): Promise { return promiseMap.get(component); } + +/** + * This helper util can be used to ensuring the component is loaded and rendered by the browser (The "componentDidLoad" Stencil lifecycle method has been called and any internal elements are focusable). + * + * Requires requires `LoadableComponent` to be implemented. + * + * A component developer can await this method before proceeding with any logic that requires a component to be loaded first and then an internal element be focused. + * + * ``` + * async setFocus(): Promise { + * await componentFocusable(this); + * this.internalElement?.focus(); + * } + * ``` + * + * @param component + * @returns Promise + */ +export async function componentFocusable(component: LoadableComponent): Promise { + await componentLoaded(component); + + if (!Build.isBrowser) { + return; + } + + forceUpdate(component); + return new Promise((resolve) => requestAnimationFrame(() => resolve())); +}