diff --git a/package.json b/package.json index 5b18fa5302..51b8032459 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ { "path": "lib/components/internal/widget-exports.js", "brotli": false, - "limit": "890 kB", + "limit": "891 kB", "ignore": "react-dom" } ], diff --git a/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx b/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx index dcc32b9095..7df9ba1aa2 100644 --- a/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx @@ -48,17 +48,36 @@ describeEachAppLayout({ themes: ['refresh-toolbar'] }, ({ size }) => { expect(globalDrawersWrapper.findDrawerById(drawerDefaults.id)!.isActive()).toBe(true); }); - test('isAppLayoutReady returns true when app layout is ready', () => { + test('isAppLayoutReady returns true when app layout is ready', async () => { expect(awsuiWidgetPlugins.isAppLayoutReady()).toBe(false); const { rerender } = renderComponent(); expect(awsuiWidgetPlugins.isAppLayoutReady()).toBe(true); + await expect(awsuiWidgetPlugins.whenAppLayoutReady()).resolves.toBe(undefined); rerender(<>); expect(awsuiWidgetPlugins.isAppLayoutReady()).toBe(false); }); + test('whenAppLayoutReady resolves when app layout is ready', async () => { + const readyPromise = awsuiWidgetPlugins.whenAppLayoutReady(); + + let isResolved = false; + readyPromise.then(() => { + isResolved = true; + }); + + expect(isResolved).toBe(false); + + const { rerender } = renderComponent(); + + rerender(<>); + + await readyPromise; + expect(isResolved).toBe(true); + }); + test('adds ai drawer to an already rendered component', () => { const { globalDrawersWrapper } = renderComponent(); expect(globalDrawersWrapper.findAiDrawerTrigger()).toBeFalsy(); diff --git a/src/internal/plugins/widget.ts b/src/internal/plugins/widget.ts index a5456b944d..252e44c7b2 100644 --- a/src/internal/plugins/widget.ts +++ b/src/internal/plugins/widget.ts @@ -1,4 +1,4 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 export * from './widget/interfaces'; -export { registerLeftDrawer, updateDrawer, isAppLayoutReady } from './widget/internal'; +export { registerLeftDrawer, updateDrawer, isAppLayoutReady, whenAppLayoutReady } from './widget/internal'; diff --git a/src/internal/plugins/widget/internal.ts b/src/internal/plugins/widget/internal.ts index 77efb3746b..a70122c207 100644 --- a/src/internal/plugins/widget/internal.ts +++ b/src/internal/plugins/widget/internal.ts @@ -6,10 +6,12 @@ import { AppLayoutMessage, AppLayoutUpdateMessage, DrawerPayload, RegisterDrawer const storageKeyMessageHandler = Symbol.for('awsui-widget-api-message-handler'); const storageKeyInitialMessages = Symbol.for('awsui-widget-api-initial-messages'); +const storageKeyReadyDeferCallbacks = Symbol.for('awsui-widget-api-ready-defer'); interface WindowWithApi extends Window { [storageKeyMessageHandler]: AppLayoutHandler | undefined; [storageKeyInitialMessages]: Array | undefined; + [storageKeyReadyDeferCallbacks]: Array<(value?: unknown) => void> | undefined; } type AppLayoutHandler = (event: AppLayoutMessage) => void; @@ -33,6 +35,8 @@ export function registerAppLayoutHandler(handler: AppLayoutHandler) { reportRuntimeApiWarning('AppLayoutWidget', 'Double registration attempt, the old handler will be overridden'); } win[storageKeyMessageHandler] = handler; + win[storageKeyReadyDeferCallbacks]?.forEach(fn => fn()); + win[storageKeyReadyDeferCallbacks] = []; return () => { win[storageKeyMessageHandler] = undefined; }; @@ -49,6 +53,18 @@ export function isAppLayoutReady() { return !!getAppLayoutMessageHandler(); } +/** + * Returns a promise that resolves once the app layout has loaded + */ +export function whenAppLayoutReady() { + if (isAppLayoutReady()) { + return Promise.resolve(); + } + const win = getWindow(); + win[storageKeyReadyDeferCallbacks] = win[storageKeyReadyDeferCallbacks] ?? []; + return new Promise(resolve => win[storageKeyReadyDeferCallbacks]?.push(resolve)); +} + /** * Registers a new runtime drawer to app layout * @param drawer