Skip to content

fix(action): correctly focus the button after rendering updates. #7255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/calcite-components/src/components/action/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
updateHostInteraction
} from "../../utils/interactive";
import {
componentLoaded,
componentFocusable,
LoadableComponent,
setComponentLoaded,
setUpLoadableComponent
Expand Down Expand Up @@ -205,8 +205,7 @@ export class Action
/** Sets focus on the component. */
@Method()
async setFocus(): Promise<void> {
await componentLoaded(this);

await componentFocusable(this);
this.buttonEl?.focus();
}

Expand Down
38 changes: 34 additions & 4 deletions packages/calcite-components/src/utils/loadable.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Build, forceUpdate } from "@stencil/core";

/**
* This helper adds support for knowing when a component has been loaded.
*
Expand Down Expand Up @@ -31,7 +33,7 @@
* //
* // --------------------------------------------------------------------------
*
* async setFocus(): Promise<void> {
* async myMethod(): Promise<void> {
* await componentLoaded(this);
* }
* ```
Expand Down Expand Up @@ -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<void> {
* async myMethod(): Promise<void> {
* await componentLoaded(this);
* }
* ```
Expand All @@ -111,3 +113,31 @@ export function setComponentLoaded(component: LoadableComponent): void {
export function componentLoaded(component: LoadableComponent): Promise<void> {
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<void> {
* await componentFocusable(this);
* this.internalElement?.focus();
* }
* ```
*
* @param component
* @returns Promise<void>
*/
export async function componentFocusable(component: LoadableComponent): Promise<void> {
await componentLoaded(component);

if (!Build.isBrowser) {
return;
}

forceUpdate(component);
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
}