Minimized drawer -- update consumers Part 3 of the 3 minimized drawer changes. Updates Console and AI Assistance panels to be aware of the new minimized drawer state. Console changes: - console.toggle now minimizes/expands the drawer instead of closing it, so the drawer persists across toggle cycles. - ConsolePanel and WrapperView save and restore the drawer's minimized state across show/hide transitions to prevent unexpected state changes when switching panels. Tests: - Unit tests for the updated console.toggle behavior. - AI Assistance panel tests for visibility events when the drawer is minimized/expanded. - E2E tests for drawer staying minimized across main-panel tab switches and for the toggle-console open/minimize/restore cycle. Original CL: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7566246 Part 1 (refactor InspectorView): https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7685970 Part 2 (core minimization feature): https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7708162 Bug: 483762280 Change-Id: I31b2ad99ebd369515a89b4071e23b2bab93a0143 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7737421 Reviewed-by: Danil Somsikov <dsv@chromium.org> Commit-Queue: Guangyue Xu <guangyue.xu@microsoft.com> Reviewed-by: Piotr Paulski <piotrpaulski@chromium.org>
diff --git a/front_end/panels/ai_assistance/AiAssistancePanel.test.ts b/front_end/panels/ai_assistance/AiAssistancePanel.test.ts index 691f5bc..9fc3d62 100644 --- a/front_end/panels/ai_assistance/AiAssistancePanel.test.ts +++ b/front_end/panels/ai_assistance/AiAssistancePanel.test.ts
@@ -1776,6 +1776,40 @@ }); } + it('should update the AI Assistance visibility status when the drawer visibility changes', async () => { + updateHostConfig({ + devToolsFreestyler: { + enabled: true, + }, + }); + + viewManagerIsViewVisibleStub.callsFake(viewName => viewName === 'elements'); + const {view} = await createAiAssistancePanel(); + + assert(view.input.state === AiAssistancePanel.ViewState.CHAT_VIEW); + assert.strictEqual( + view.input.props.conversationType, AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING); + + viewManagerIsViewVisibleStub.returns(false); + UI.ViewManager.ViewManager.instance().dispatchEventToListeners(UI.ViewManager.Events.VIEW_VISIBILITY_CHANGED, { + location: 'drawer', + revealedViewId: undefined, + hiddenViewId: 'elements', + }); + let nextInput = await view.nextInput; + assert(nextInput.state === AiAssistancePanel.ViewState.EXPLORE_VIEW); + + viewManagerIsViewVisibleStub.callsFake(viewName => viewName === 'elements'); + UI.ViewManager.ViewManager.instance().dispatchEventToListeners(UI.ViewManager.Events.VIEW_VISIBILITY_CHANGED, { + location: 'drawer', + revealedViewId: 'elements', + hiddenViewId: undefined, + }); + nextInput = await view.nextInput; + assert(nextInput.state === AiAssistancePanel.ViewState.CHAT_VIEW); + assert.strictEqual(nextInput.props.conversationType, AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING); + }); + it('should refresh its state when moved', async () => { updateHostConfig({ devToolsFreestyler: {
diff --git a/front_end/panels/console/ConsolePanel.ts b/front_end/panels/console/ConsolePanel.ts index e8cf620..364793d0a7 100644 --- a/front_end/panels/console/ConsolePanel.ts +++ b/front_end/panels/console/ConsolePanel.ts
@@ -41,6 +41,7 @@ export class ConsolePanel extends UI.Panel.Panel { private readonly view: ConsoleView; + #drawerWasMinimized = false; constructor() { super('console'); this.view = ConsoleView.instance(); @@ -64,9 +65,11 @@ override wasShown(): void { super.wasShown(); + const inspectorView = UI.InspectorView.InspectorView.instance(); + this.#drawerWasMinimized = inspectorView.isDrawerMinimized(); const wrapper = wrapperViewInstance; if (wrapper?.isShowing()) { - UI.InspectorView.InspectorView.instance().setDrawerMinimized(true); + inspectorView.setDrawerMinimized(true); } this.view.show(this.element); ConsolePanel.updateContextFlavor(); @@ -74,12 +77,17 @@ override willHide(): void { super.willHide(); + const inspectorView = UI.InspectorView.InspectorView.instance(); // The minimized drawer has 0 height, and showing Console inside may set // Console's scrollTop to 0. Unminimize before calling show to avoid this. - UI.InspectorView.InspectorView.instance().setDrawerMinimized(false); + // Restore the previous minimized state afterwards. + inspectorView.setDrawerMinimized(false); if (wrapperViewInstance) { wrapperViewInstance.showViewInWrapper(); } + if (this.#drawerWasMinimized) { + inspectorView.setDrawerMinimized(true); + } ConsolePanel.updateContextFlavor(); } @@ -117,7 +125,6 @@ override willHide(): void { super.willHide(); - UI.InspectorView.InspectorView.instance().setDrawerMinimized(false); ConsolePanel.updateContextFlavor(); }
diff --git a/front_end/panels/console/ConsoleView.test.ts b/front_end/panels/console/ConsoleView.test.ts index cd53ad5..99ec516 100644 --- a/front_end/panels/console/ConsoleView.test.ts +++ b/front_end/panels/console/ConsoleView.test.ts
@@ -38,6 +38,100 @@ consoleView.detach(); }); + it('expands a minimized drawer when toggling console', () => { + const inspectorView = UI.InspectorView.InspectorView.instance({forceNew: true}); + const drawerVisibleStub = sinon.stub(inspectorView, 'drawerVisible').returns(true); + const isDrawerMinimizedStub = sinon.stub(inspectorView, 'isDrawerMinimized').returns(true); + const setDrawerMinimizedStub = sinon.stub(inspectorView, 'setDrawerMinimized'); + const hasFocusStub = sinon.stub(consoleView, 'hasFocus').returns(false); + const bringToFrontStub = sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'bringToFront'); + const showStub = sinon.stub(Common.Console.Console.instance(), 'show'); + const focusPromptStub = sinon.stub(consoleView, 'focusPrompt'); + + const delegate = new Console.ConsoleView.ActionDelegate(); + assert.isTrue(delegate.handleAction({} as UI.Context.Context, 'console.toggle')); + + sinon.assert.calledOnceWithExactly(setDrawerMinimizedStub, false); + sinon.assert.calledOnce(showStub); + sinon.assert.calledOnce(focusPromptStub); + sinon.assert.calledOnce(bringToFrontStub); + + drawerVisibleStub.restore(); + isDrawerMinimizedStub.restore(); + setDrawerMinimizedStub.restore(); + hasFocusStub.restore(); + bringToFrontStub.restore(); + showStub.restore(); + focusPromptStub.restore(); + UI.InspectorView.InspectorView.removeInstance(); + }); + + it('minimizes drawer when console is already shown and focused in expanded drawer', () => { + const inspectorView = UI.InspectorView.InspectorView.instance({forceNew: true}); + const drawerVisibleStub = sinon.stub(inspectorView, 'drawerVisible').returns(true); + const isDrawerMinimizedStub = sinon.stub(inspectorView, 'isDrawerMinimized').returns(false); + const setDrawerMinimizedStub = sinon.stub(inspectorView, 'setDrawerMinimized'); + const minimizeDrawerStub = sinon.stub(inspectorView, 'minimizeDrawer'); + const isShowingStub = sinon.stub(consoleView, 'isShowing').returns(true); + const hasFocusStub = sinon.stub(consoleView, 'hasFocus').returns(true); + const bringToFrontStub = sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'bringToFront'); + const showStub = sinon.stub(Common.Console.Console.instance(), 'show'); + const focusPromptStub = sinon.stub(consoleView, 'focusPrompt'); + + const delegate = new Console.ConsoleView.ActionDelegate(); + assert.isTrue(delegate.handleAction({} as UI.Context.Context, 'console.toggle')); + + sinon.assert.notCalled(setDrawerMinimizedStub); + sinon.assert.calledOnce(minimizeDrawerStub); + sinon.assert.notCalled(showStub); + sinon.assert.notCalled(focusPromptStub); + sinon.assert.notCalled(bringToFrontStub); + + drawerVisibleStub.restore(); + isDrawerMinimizedStub.restore(); + setDrawerMinimizedStub.restore(); + minimizeDrawerStub.restore(); + isShowingStub.restore(); + hasFocusStub.restore(); + bringToFrontStub.restore(); + showStub.restore(); + focusPromptStub.restore(); + UI.InspectorView.InspectorView.removeInstance(); + }); + + it('focuses console prompt when drawer is expanded but console is not focused', () => { + const inspectorView = UI.InspectorView.InspectorView.instance({forceNew: true}); + const drawerVisibleStub = sinon.stub(inspectorView, 'drawerVisible').returns(true); + const isDrawerMinimizedStub = sinon.stub(inspectorView, 'isDrawerMinimized').returns(false); + const setDrawerMinimizedStub = sinon.stub(inspectorView, 'setDrawerMinimized'); + const minimizeDrawerStub = sinon.stub(inspectorView, 'minimizeDrawer'); + const isShowingStub = sinon.stub(consoleView, 'isShowing').returns(true); + const hasFocusStub = sinon.stub(consoleView, 'hasFocus').returns(false); + const bringToFrontStub = sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'bringToFront'); + const showStub = sinon.stub(Common.Console.Console.instance(), 'show'); + const focusPromptStub = sinon.stub(consoleView, 'focusPrompt'); + + const delegate = new Console.ConsoleView.ActionDelegate(); + assert.isTrue(delegate.handleAction({} as UI.Context.Context, 'console.toggle')); + + sinon.assert.notCalled(minimizeDrawerStub); + sinon.assert.notCalled(setDrawerMinimizedStub); + sinon.assert.calledOnce(bringToFrontStub); + sinon.assert.calledOnce(showStub); + sinon.assert.calledOnce(focusPromptStub); + + drawerVisibleStub.restore(); + isDrawerMinimizedStub.restore(); + setDrawerMinimizedStub.restore(); + minimizeDrawerStub.restore(); + isShowingStub.restore(); + hasFocusStub.restore(); + bringToFrontStub.restore(); + showStub.restore(); + focusPromptStub.restore(); + UI.InspectorView.InspectorView.removeInstance(); + }); + it('adds a title to every checkbox label in the settings view', async () => { const consoleSettingsCheckboxes = consoleView.element.querySelector('devtools-toolbar')!.querySelectorAll('devtools-checkbox');
diff --git a/front_end/panels/console/ConsoleView.ts b/front_end/panels/console/ConsoleView.ts index 593c7e8..6fa14c9 100644 --- a/front_end/panels/console/ConsoleView.ts +++ b/front_end/panels/console/ConsoleView.ts
@@ -1959,15 +1959,22 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate { handleAction(_context: UI.Context.Context, actionId: string): boolean { switch (actionId) { - case 'console.toggle': - if (ConsoleView.instance().hasFocus() && UI.InspectorView.InspectorView.instance().drawerVisible()) { - UI.InspectorView.InspectorView.instance().closeDrawer(); + case 'console.toggle': { + const inspectorView = UI.InspectorView.InspectorView.instance(); + const consoleView = ConsoleView.instance(); + if (inspectorView.drawerVisible() && !inspectorView.isDrawerMinimized() && consoleView.isShowing() && + consoleView.hasFocus()) { + inspectorView.minimizeDrawer(); return true; } + if (inspectorView.drawerVisible() && inspectorView.isDrawerMinimized()) { + inspectorView.setDrawerMinimized(false); + } Host.InspectorFrontendHost.InspectorFrontendHostInstance.bringToFront(); Common.Console.Console.instance().show(); - ConsoleView.instance().focusPrompt(); + consoleView.focusPrompt(); return true; + } case 'console.clear': ConsoleView.instance().clearConsole(); return true;
diff --git a/test/e2e/inspector/BUILD.gn b/test/e2e/inspector/BUILD.gn index a4158b6..c308207 100644 --- a/test/e2e/inspector/BUILD.gn +++ b/test/e2e/inspector/BUILD.gn
@@ -6,5 +6,8 @@ ts_e2e_library("inspector") { sources = [ "drawer.test.ts" ] - deps = [ "../../shared" ] + deps = [ + "../../shared", + "../helpers", + ] }
diff --git a/test/e2e/inspector/drawer.test.ts b/test/e2e/inspector/drawer.test.ts index 374bd0d..766dd41 100644 --- a/test/e2e/inspector/drawer.test.ts +++ b/test/e2e/inspector/drawer.test.ts
@@ -4,6 +4,7 @@ import {assert} from 'chai'; +import {runCommandWithQuickOpen} from '../helpers/quick_open-helpers.js'; import type {DevToolsPage} from '../shared/frontend-helper.js'; const MINIMIZE_BUTTON_SELECTOR = '[aria-label="Minimize drawer"]'; @@ -431,6 +432,42 @@ assert.exists(expandButton, 'Drawer should remain minimized after toggling orientation back and forth'); }); + it('drawer stays minimized when switching main panel tabs', async ({devToolsPage}) => { + // Show the drawer + await devToolsPage.pressKey('Escape'); + await devToolsPage.waitFor(DRAWER_SELECTOR); + + // Minimize the drawer + await devToolsPage.click(MINIMIZE_BUTTON_SELECTOR); + await devToolsPage.waitFor(EXPAND_BUTTON_SELECTOR); + + // Click Console tab in the main tab bar + await devToolsPage.click('#tab-console'); + // Switch to Elements tab + await devToolsPage.click('#tab-elements'); + + // The drawer should still be minimized (expand button visible, not minimize button) + const expandButton = await devToolsPage.waitFor(EXPAND_BUTTON_SELECTOR); + assert.exists(expandButton, 'Drawer should remain minimized after switching main panel tabs'); + }); + + it('toggle console command opens, minimizes, and restores the drawer', async ({devToolsPage}) => { + await devToolsPage.click('#tab-elements'); + await devToolsPage.waitForNone(DRAWER_SELECTOR); + + await runCommandWithQuickOpen('Toggle console', devToolsPage); + await devToolsPage.waitFor(DRAWER_SELECTOR); + await devToolsPage.waitFor(MINIMIZE_BUTTON_SELECTOR); + + await runCommandWithQuickOpen('Toggle console', devToolsPage); + await devToolsPage.waitFor(DRAWER_SELECTOR); + await devToolsPage.waitFor(EXPAND_BUTTON_SELECTOR); + + await runCommandWithQuickOpen('Toggle console', devToolsPage); + await devToolsPage.waitFor(DRAWER_SELECTOR); + await devToolsPage.waitFor(MINIMIZE_BUTTON_SELECTOR); + }); + it('clicking drawer console tab minimizes expanded drawer when main console is active', async ({devToolsPage}) => { // Move Elements to drawer so drawer has at least two tabs and Console tab is clickable. await clickOnContextMenuItemFromTab('#tab-elements', MOVE_TO_DRAWER_SELECTOR, devToolsPage);