diff --git a/pages/app-layout/utils/external-global-left-panel-widget.tsx b/pages/app-layout/utils/external-global-left-panel-widget.tsx
index 5283d3362f..33ca05802b 100644
--- a/pages/app-layout/utils/external-global-left-panel-widget.tsx
+++ b/pages/app-layout/utils/external-global-left-panel-widget.tsx
@@ -139,4 +139,8 @@ registerLeftDrawer({
onHeaderActionClick: ({ detail }) => {
console.log('onHeaderActionClick: ', detail);
},
+
+ onToggleFocusMode: ({ detail }) => {
+ console.log('onToggleFocusMode: ', detail);
+ },
});
diff --git a/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx b/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx
index bbf9714607..7f80f87403 100644
--- a/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx
+++ b/src/app-layout/__tests__/runtime-drawers-widgetized.test.tsx
@@ -257,6 +257,31 @@ describeEachAppLayout({ themes: ['refresh-toolbar'] }, ({ size }) => {
}
);
+ test(`calls onToggleFocusMode handler by entering / exiting focus mode in left runtime drawer)`, () => {
+ const drawerId = 'global-drawer';
+ const onToggleFocusMode = jest.fn();
+ awsuiWidgetPlugins.registerLeftDrawer({
+ ...drawerDefaults,
+ id: drawerId,
+ isExpandable: true,
+ onToggleFocusMode: event => onToggleFocusMode(event.detail),
+ });
+ const renderProps = renderComponent();
+ const { globalDrawersWrapper } = renderProps;
+
+ globalDrawersWrapper.findAiDrawerTrigger()!.click();
+ if (size === 'mobile') {
+ expect(globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId)).toBeFalsy();
+ } else {
+ createWrapper().findButtonGroup()!.findButtonById('expand')!.click();
+ expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(true);
+ expect(onToggleFocusMode).toHaveBeenCalledWith({ isExpanded: true });
+ createWrapper().findButtonGroup()!.findButtonById('expand')!.click();
+ expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false);
+ expect(onToggleFocusMode).toHaveBeenCalledWith({ isExpanded: false });
+ }
+ });
+
describe('metrics', () => {
let sendPanoramaMetricSpy: jest.SpyInstance;
beforeEach(() => {
diff --git a/src/app-layout/runtime-drawer/index.tsx b/src/app-layout/runtime-drawer/index.tsx
index deb1eb29c7..63d932c506 100644
--- a/src/app-layout/runtime-drawer/index.tsx
+++ b/src/app-layout/runtime-drawer/index.tsx
@@ -149,6 +149,7 @@ export const mapRuntimeConfigToAiDrawer = (
onToggle?: NonCancelableEventHandler;
headerActions?: ReadonlyArray;
exitExpandedModeTrigger?: React.ReactNode;
+ onToggleFocusMode?: NonCancelableEventHandler<{ isExpanded: boolean }>;
} => {
const { mountContent, unmountContent, trigger, exitExpandedModeTrigger, ...runtimeDrawer } = runtimeConfig;
diff --git a/src/app-layout/visual-refresh-toolbar/interfaces.ts b/src/app-layout/visual-refresh-toolbar/interfaces.ts
index d5ed39095a..5d91283079 100644
--- a/src/app-layout/visual-refresh-toolbar/interfaces.ts
+++ b/src/app-layout/visual-refresh-toolbar/interfaces.ts
@@ -27,6 +27,7 @@ export type InternalDrawer = AppLayoutProps.Drawer & {
headerActions?: ReadonlyArray;
onHeaderActionClick?: NonCancelableEventHandler;
position?: 'side' | 'bottom';
+ onToggleFocusMode?: NonCancelableEventHandler<{ isExpanded: boolean }>;
};
// Widgetization notice: structures in this file are shared multiple app layout instances, possibly different minor versions.
diff --git a/src/app-layout/visual-refresh-toolbar/state/use-app-layout.tsx b/src/app-layout/visual-refresh-toolbar/state/use-app-layout.tsx
index 2590968f34..48a5e7f184 100644
--- a/src/app-layout/visual-refresh-toolbar/state/use-app-layout.tsx
+++ b/src/app-layout/visual-refresh-toolbar/state/use-app-layout.tsx
@@ -71,7 +71,7 @@ export const useAppLayout = (
const [navigationAnimationDisabled, setNavigationAnimationDisabled] = useState(true);
const [splitPanelAnimationDisabled, setSplitPanelAnimationDisabled] = useState(true);
const [isNested, setIsNested] = useState(false);
- const [expandedDrawerId, setExpandedDrawerId] = useState(null);
+ const [expandedDrawerId, setInternalExpandedDrawerId] = useState(null);
const rootRefInternal = useRef(null);
// This workaround ensures the ref is defined before checking if the app layout is nested.
// On initial render, the ref might be undefined because this component loads asynchronously via the widget API.
@@ -90,6 +90,16 @@ export const useAppLayout = (
fireNonCancelableEvent(onToolsChange, { open });
};
+ const setExpandedDrawerId = (value: string | null) => {
+ setInternalExpandedDrawerId(value);
+
+ if (aiDrawer?.onToggleFocusMode && (value === aiDrawer?.id || value === null)) {
+ fireNonCancelableEvent(aiDrawer.onToggleFocusMode, {
+ isExpanded: !!value,
+ });
+ }
+ };
+
const onGlobalDrawerFocus = (drawerId: string, open: boolean) => {
globalDrawersFocusControl.setFocus({ force: true, drawerId, open });
};
diff --git a/src/internal/plugins/widget/interfaces.ts b/src/internal/plugins/widget/interfaces.ts
index 5ffcaf7431..e9a919eee7 100644
--- a/src/internal/plugins/widget/interfaces.ts
+++ b/src/internal/plugins/widget/interfaces.ts
@@ -50,6 +50,7 @@ export interface DrawerPayload {
unmountHeader?: (container: HTMLElement) => void;
headerActions?: ReadonlyArray;
onHeaderActionClick?: NonCancelableEventHandler;
+ onToggleFocusMode?: NonCancelableEventHandler<{ isExpanded: boolean }>;
position?: 'side' | 'bottom';
}