diff --git a/pages/app-layout/utils/external-widget.tsx b/pages/app-layout/utils/external-widget.tsx
index a81ca17896..ea64dae6c0 100644
--- a/pages/app-layout/utils/external-widget.tsx
+++ b/pages/app-layout/utils/external-widget.tsx
@@ -94,40 +94,11 @@ const Counter: React.FC = ({ children }) => {
);
};
-class CounterStateManager {
- private isPaused = false;
- private pauseCallback: ((isPaused: boolean) => void) | null = null;
-
- private onPauseStateChange() {
- if (this.pauseCallback) {
- this.pauseCallback(this.isPaused);
- }
- }
-
- registerPauseCallback(callback: (isPaused: boolean) => void) {
- this.pauseCallback = callback;
- }
-
- unregisterPauseCallback() {
- this.pauseCallback = null;
- }
-
- pause() {
- this.isPaused = true;
- this.onPauseStateChange();
- }
-
- resume() {
- this.isPaused = false;
- this.onPauseStateChange();
- }
-}
-
-const autoIncrementState = new CounterStateManager();
-
-const AutoIncrementCounter: React.FC = ({ children }) => {
+const AutoIncrementCounter: React.FC<{
+ onVisibilityChange?: (callback: (isVisible: boolean) => void) => () => void;
+}> = ({ children, onVisibilityChange }) => {
const [count, setCount] = useState(0);
- const [isPaused, setIsPaused] = useState(false);
+ const [isPaused, setIsPaused] = useState(true);
useEffect(() => {
const interval = setInterval(() => {
@@ -140,12 +111,16 @@ const AutoIncrementCounter: React.FC = ({ children }) => {
}, [isPaused]);
useEffect(() => {
- autoIncrementState.registerPauseCallback(isPaused => {
- setIsPaused(isPaused);
- });
-
- return () => autoIncrementState.unregisterPauseCallback();
- }, []);
+ if (onVisibilityChange) {
+ const unsubscribe = onVisibilityChange((isVisible: boolean) => {
+ setIsPaused(!isVisible);
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }
+ }, [onVisibilityChange]);
return (
@@ -163,8 +138,6 @@ awsuiPlugins.appLayout.registerDrawer({
resizable: true,
defaultSize: 350,
preserveInactiveContent: true,
- onShow: () => autoIncrementState.resume(),
- onHide: () => autoIncrementState.pause(),
ariaLabels: {
closeButton: 'Close button',
@@ -184,8 +157,16 @@ awsuiPlugins.appLayout.registerDrawer({
console.log('resize', event.detail);
},
- mountContent: container => {
- ReactDOM.render(
global widget content circle 1, container);
+ mountContent: (
+ container: HTMLElement,
+ onVisibilityChange?: (callback: (isVisible: boolean) => void) => () => void
+ ) => {
+ ReactDOM.render(
+
+ global widget content circle 1
+ ,
+ container
+ );
},
unmountContent: container => unmountComponentAtNode(container),
});
diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx
index a6357fd2c9..3cd077da93 100644
--- a/src/app-layout/__tests__/runtime-drawers.test.tsx
+++ b/src/app-layout/__tests__/runtime-drawers.test.tsx
@@ -1065,17 +1065,26 @@ describe('toolbar mode only features', () => {
expect(globalDrawersWrapper.findDrawerById('global-drawer-1')!.isActive()).toBe(true);
});
- test('should call onShow and onHide when global drawer with preserveInactiveContent is opened and closed', async () => {
+ test('should call visibilityChange callback when global drawer with preserveInactiveContent is opened and closed', async () => {
const onShow = jest.fn();
const onHide = jest.fn();
awsuiPlugins.appLayout.registerDrawer({
...drawerDefaults,
id: 'global-drawer-1',
type: 'global',
- mountContent: container => (container.textContent = 'global drawer content 1'),
+ mountContent: (container, onVisibilityChange) => {
+ if (onVisibilityChange) {
+ onVisibilityChange((isVisible: boolean) => {
+ if (isVisible) {
+ onShow();
+ } else {
+ onHide();
+ }
+ });
+ }
+ container.textContent = 'global drawer content 1';
+ },
preserveInactiveContent: true,
- onShow,
- onHide,
});
const { wrapper, globalDrawersWrapper } = await renderComponent(
);
diff --git a/src/app-layout/interfaces.ts b/src/app-layout/interfaces.ts
index 362e611bd3..0f48c32ba2 100644
--- a/src/app-layout/interfaces.ts
+++ b/src/app-layout/interfaces.ts
@@ -308,8 +308,6 @@ export namespace AppLayoutProps {
defaultSize?: number;
onResize?: NonCancelableEventHandler<{ size: number }>;
preserveInactiveContent?: boolean;
- onShow?: NonCancelableEventHandler;
- onHide?: NonCancelableEventHandler;
}
export interface DrawerAriaLabels {
diff --git a/src/app-layout/runtime-api.tsx b/src/app-layout/runtime-api.tsx
index 2f3d751298..d37945bc6d 100644
--- a/src/app-layout/runtime-api.tsx
+++ b/src/app-layout/runtime-api.tsx
@@ -2,10 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
-import { fireNonCancelableEvent } from '../internal/events';
+import { fireNonCancelableEvent, NonCancelableEventHandler } from '../internal/events';
import { DrawerConfig as RuntimeDrawerConfig } from '../internal/plugins/controllers/drawers';
import { RuntimeContentWrapper } from '../internal/plugins/helpers';
import { sortByPriority } from '../internal/plugins/helpers/utils';
+import VisibilityStateManager from '../internal/plugins/helpers/visibility-state-manager';
import { AppLayoutProps } from './interfaces';
export interface DrawersLayout {
@@ -13,6 +14,8 @@ export interface DrawersLayout {
after: Array
;
}
+const visibilityStateManagerMap = new Map();
+
export function convertRuntimeDrawers(drawers: Array): DrawersLayout {
const converted = drawers.map(
({
@@ -20,22 +23,43 @@ export function convertRuntimeDrawers(drawers: Array): Draw
unmountContent,
trigger,
...runtimeDrawer
- }): AppLayoutProps.Drawer & { orderPriority?: number } => ({
- ...runtimeDrawer,
- ariaLabels: { drawerName: runtimeDrawer.ariaLabels.content ?? '', ...runtimeDrawer.ariaLabels },
- trigger: {
- iconSvg: (
- // eslint-disable-next-line react/no-danger
-
+ }): AppLayoutProps.Drawer & {
+ orderPriority?: number;
+ onShow?: NonCancelableEventHandler;
+ onHide?: NonCancelableEventHandler;
+ } => {
+ let visibilityStateManager: VisibilityStateManager;
+ if (visibilityStateManagerMap.has(runtimeDrawer.id)) {
+ visibilityStateManager = visibilityStateManagerMap.get(runtimeDrawer.id)!;
+ } else {
+ visibilityStateManager = new VisibilityStateManager();
+ visibilityStateManagerMap.set(runtimeDrawer.id, visibilityStateManager);
+ }
+
+ return {
+ ...runtimeDrawer,
+ ariaLabels: { drawerName: runtimeDrawer.ariaLabels.content ?? '', ...runtimeDrawer.ariaLabels },
+ trigger: {
+ iconSvg: (
+ // eslint-disable-next-line react/no-danger
+
+ ),
+ },
+ content: (
+
),
- },
- content: (
-
- ),
- onResize: event => {
- fireNonCancelableEvent(runtimeDrawer.onResize, { size: event.detail.size, id: runtimeDrawer.id });
- },
- })
+ onResize: event => {
+ fireNonCancelableEvent(runtimeDrawer.onResize, { size: event.detail.size, id: runtimeDrawer.id });
+ },
+ onShow: visibilityStateManager.show,
+ onHide: visibilityStateManager.hide,
+ };
+ }
);
const sorted = sortByPriority(converted);
return {
diff --git a/src/app-layout/visual-refresh-toolbar/drawer/index.tsx b/src/app-layout/visual-refresh-toolbar/drawer/index.tsx
index 275abe4a23..aa1fa5d2b6 100644
--- a/src/app-layout/visual-refresh-toolbar/drawer/index.tsx
+++ b/src/app-layout/visual-refresh-toolbar/drawer/index.tsx
@@ -5,7 +5,7 @@ import clsx from 'clsx';
import { InternalButton } from '../../../button/internal';
import PanelResizeHandle from '../../../internal/components/panel-resize-handle';
-import { fireNonCancelableEvent } from '../../../internal/events';
+import { fireNonCancelableEvent, NonCancelableEventHandler } from '../../../internal/events';
import customCssProps from '../../../internal/generated/custom-css-properties';
import { usePrevious } from '../../../internal/hooks/use-previous';
import { createWidgetizedComponent } from '../../../internal/widgets';
@@ -131,7 +131,9 @@ export function AppLayoutDrawerImplementation({ appLayoutInternals }: AppLayoutD
interface AppLayoutGlobalDrawerImplementationProps {
appLayoutInternals: AppLayoutInternals;
show: boolean;
- activeGlobalDrawer: AppLayoutProps.Drawer | undefined;
+ activeGlobalDrawer:
+ | (AppLayoutProps.Drawer & { onShow?: NonCancelableEventHandler; onHide?: NonCancelableEventHandler })
+ | undefined;
}
export function AppLayoutGlobalDrawerImplementation({
diff --git a/src/internal/plugins/controllers/drawers.ts b/src/internal/plugins/controllers/drawers.ts
index 2bbd9f75e1..277af9eba7 100644
--- a/src/internal/plugins/controllers/drawers.ts
+++ b/src/internal/plugins/controllers/drawers.ts
@@ -21,11 +21,12 @@ export interface DrawerConfig {
trigger: {
iconSvg: string;
};
- mountContent: (container: HTMLElement) => void;
+ mountContent: (
+ container: HTMLElement,
+ onVisibilityChange?: (callback: (isVisible: boolean) => void) => () => void
+ ) => void;
unmountContent: (container: HTMLElement) => void;
preserveInactiveContent?: boolean;
- onShow?: NonCancelableEventHandler;
- onHide?: NonCancelableEventHandler;
}
export type UpdateDrawerConfig = Pick;
diff --git a/src/internal/plugins/helpers/runtime-content-wrapper.tsx b/src/internal/plugins/helpers/runtime-content-wrapper.tsx
index df18772e8b..80565a56e5 100644
--- a/src/internal/plugins/helpers/runtime-content-wrapper.tsx
+++ b/src/internal/plugins/helpers/runtime-content-wrapper.tsx
@@ -2,18 +2,27 @@
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect, useRef } from 'react';
+type VisibilityCallback = (isVisible: boolean) => void;
+
interface RuntimeContentWrapperProps {
- mountContent: (container: HTMLElement) => void;
+ mountContent: (container: HTMLElement, visibilityCallback?: (callback: VisibilityCallback) => () => void) => void;
unmountContent: (container: HTMLElement) => void;
+ registerVisibilityCallback?: (callback: VisibilityCallback) => () => void;
}
-export function RuntimeContentWrapper({ mountContent, unmountContent }: RuntimeContentWrapperProps) {
+export function RuntimeContentWrapper({
+ mountContent,
+ unmountContent,
+ registerVisibilityCallback,
+}: RuntimeContentWrapperProps) {
const ref = useRef(null);
useEffect(() => {
const container = ref.current!;
- mountContent(container);
- return () => unmountContent(container);
+ mountContent(container, registerVisibilityCallback);
+ return () => {
+ unmountContent(container);
+ };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
diff --git a/src/internal/plugins/helpers/visibility-state-manager.ts b/src/internal/plugins/helpers/visibility-state-manager.ts
new file mode 100644
index 0000000000..cd82b87929
--- /dev/null
+++ b/src/internal/plugins/helpers/visibility-state-manager.ts
@@ -0,0 +1,34 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * This class is used to manage the visibility state of the runtime drawers.
+ */
+export default class VisibilityStateManager {
+ isVisible = false;
+ visibilityCallback: ((isPaused: boolean) => void) | null = null;
+
+ private onVisibleStateChange = () => {
+ if (this.visibilityCallback) {
+ this.visibilityCallback(this.isVisible);
+ }
+ };
+
+ registerVisibilityCallback = (callback: (isVisible: boolean) => void) => {
+ this.visibilityCallback = callback;
+
+ return () => {
+ this.visibilityCallback = null;
+ };
+ };
+
+ show = () => {
+ this.isVisible = true;
+ this.onVisibleStateChange();
+ };
+
+ hide = () => {
+ this.isVisible = false;
+ this.onVisibleStateChange();
+ };
+}