Path: blob/main/src/vs/base/test/browser/ui/toolbar/toolbar.test.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { IContextMenuProvider } from '../../../../browser/contextmenu.js';7import { ActionBar } from '../../../../browser/ui/actionbar/actionbar.js';8import { BaseActionViewItem } from '../../../../browser/ui/actionbar/actionViewItems.js';9import { ToggleMenuAction, ToolBar } from '../../../../browser/ui/toolbar/toolbar.js';10import { Action, IAction } from '../../../../common/actions.js';11import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';1213class FixedWidthActionViewItem extends BaseActionViewItem {1415constructor(action: IAction, private readonly width: number) {16super(undefined, action);17}1819override render(container: HTMLElement): void {20super.render(container);21container.style.width = `${this.width}px`;22container.style.boxSizing = 'border-box';23container.style.overflow = 'hidden';24container.style.whiteSpace = 'nowrap';25container.textContent = this.action.label;26}27}2829class TestToolBar extends ToolBar {30get actionBarForTest(): Pick<ActionBar, 'getWidth' | 'getAction'> {31return this.actionBar;32}33}3435const contextMenuProvider: IContextMenuProvider = {36showContextMenu: () => { }37};3839suite('ToolBar', () => {40const store = ensureNoDisposablesAreLeakedInTestSuite();4142let container: HTMLElement;4344setup(() => {45container = document.createElement('div');46container.style.width = '273px';47document.body.appendChild(container);48});4950teardown(() => {51container.remove();52});5354test('keeps the last primary action shrinkable when overflow is inserted', () => {55const widths = new Map<string, number>([56['workbench.action.chat.attachContext', 22],57['workbench.action.chat.openModePicker', 75],58['workbench.action.chat.openModelPicker', 271],59['workbench.action.chat.configureTools', 22],60[ToggleMenuAction.ID, 22],61]);6263const toolbar = store.add(new TestToolBar(container, contextMenuProvider, {64responsiveBehavior: {65enabled: true,66kind: 'last',67minItems: 1,68actionMinWidth: 22,69},70actionViewItemProvider: action => {71const width = widths.get(action.id);72return typeof width === 'number' ? new FixedWidthActionViewItem(action, width) : undefined;73}74}));75const actionBar = toolbar.actionBarForTest;76const originalGetWidth = actionBar.getWidth.bind(actionBar);77actionBar.getWidth = (index: number) => {78const action = actionBar.getAction(index);79return action ? (widths.get(action.id) ?? originalGetWidth(index)) : originalGetWidth(index);80};8182const originalGetBoundingClientRect = toolbar.getElement().getBoundingClientRect.bind(toolbar.getElement());83(toolbar.getElement() as HTMLElement & { getBoundingClientRect(): DOMRect }).getBoundingClientRect = () => ({84...originalGetBoundingClientRect(),85width: 273,86right: 273,87left: 0,88x: 0,89y: 0,90top: 0,91bottom: 0,92height: 0,93toJSON() {94return {};95}96});9798const actions = [99store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),100store.add(new Action('workbench.action.chat.openModePicker', 'Open Agent Picker')),101store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),102store.add(new Action('workbench.action.chat.configureTools', 'Configure Tools...')),103];104105toolbar.setActions(actions);106107assert.strictEqual(toolbar.getItemsLength(), 4);108assert.strictEqual(toolbar.getItemAction(0)?.id, 'workbench.action.chat.attachContext');109assert.strictEqual(toolbar.getItemAction(1)?.id, 'workbench.action.chat.openModePicker');110assert.strictEqual(toolbar.getItemAction(2)?.id, 'workbench.action.chat.openModelPicker');111assert.strictEqual(toolbar.getItemAction(3)?.id, ToggleMenuAction.ID);112assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), true);113});114115test('applies per-action responsive min widths', () => {116const toolbar = store.add(new ToolBar(container, contextMenuProvider, {117responsiveBehavior: {118enabled: true,119kind: 'last',120minItems: 1,121actionMinWidth: 22,122getActionMinWidth: action => action.id === 'workbench.action.chat.openModelPicker' ? 28 : undefined,123},124actionViewItemProvider: action => new FixedWidthActionViewItem(action, 22)125}));126127const actions = [128store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),129store.add(new Action('workbench.action.chat.openModePicker', 'Open Agent Picker')),130store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),131];132133toolbar.setActions(actions);134135assert.strictEqual(toolbar.getElement().style.getPropertyValue('--vscode-toolbar-action-min-width'), '28px');136});137138test('relayout re-evaluates responsive overflow after action width changes', () => {139const widths = new Map<string, number>([140['workbench.action.chat.attachContext', 22],141['workbench.action.chat.openModePicker', 22],142['workbench.action.chat.openModelPicker', 50],143[ToggleMenuAction.ID, 22],144]);145146const toolbar = store.add(new TestToolBar(container, contextMenuProvider, {147responsiveBehavior: {148enabled: true,149kind: 'last',150minItems: 1,151actionMinWidth: 22,152},153actionViewItemProvider: action => {154const width = widths.get(action.id);155return typeof width === 'number' ? new FixedWidthActionViewItem(action, width) : undefined;156}157}));158const actionBar = toolbar.actionBarForTest;159const originalGetWidth = actionBar.getWidth.bind(actionBar);160actionBar.getWidth = (index: number) => {161const action = actionBar.getAction(index);162return action ? (widths.get(action.id) ?? originalGetWidth(index)) : originalGetWidth(index);163};164165const originalGetBoundingClientRect = toolbar.getElement().getBoundingClientRect.bind(toolbar.getElement());166(toolbar.getElement() as HTMLElement & { getBoundingClientRect(): DOMRect }).getBoundingClientRect = () => ({167...originalGetBoundingClientRect(),168width: 110,169right: 110,170left: 0,171x: 0,172y: 0,173top: 0,174bottom: 0,175height: 0,176toJSON() {177return {};178}179});180181const actions = [182store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),183store.add(new Action('workbench.action.chat.openModePicker', 'Open Mode Picker')),184store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),185];186187toolbar.setActions(actions);188189assert.strictEqual(toolbar.getItemsLength(), 3);190assert.strictEqual(toolbar.getItemAction(2)?.id, 'workbench.action.chat.openModelPicker');191assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), false);192193widths.set('workbench.action.chat.openModePicker', 80);194toolbar.relayout();195196assert.strictEqual(toolbar.getItemsLength(), 3);197assert.strictEqual(toolbar.getItemAction(0)?.id, 'workbench.action.chat.attachContext');198assert.strictEqual(toolbar.getItemAction(1)?.id, 'workbench.action.chat.openModePicker');199assert.strictEqual(toolbar.getItemAction(2)?.id, ToggleMenuAction.ID);200assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), true);201});202});203204205