Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/browser/ui/toolbar/toolbar.test.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { IContextMenuProvider } from '../../../../browser/contextmenu.js';
8
import { ActionBar } from '../../../../browser/ui/actionbar/actionbar.js';
9
import { BaseActionViewItem } from '../../../../browser/ui/actionbar/actionViewItems.js';
10
import { ToggleMenuAction, ToolBar } from '../../../../browser/ui/toolbar/toolbar.js';
11
import { Action, IAction } from '../../../../common/actions.js';
12
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';
13
14
class FixedWidthActionViewItem extends BaseActionViewItem {
15
16
constructor(action: IAction, private readonly width: number) {
17
super(undefined, action);
18
}
19
20
override render(container: HTMLElement): void {
21
super.render(container);
22
container.style.width = `${this.width}px`;
23
container.style.boxSizing = 'border-box';
24
container.style.overflow = 'hidden';
25
container.style.whiteSpace = 'nowrap';
26
container.textContent = this.action.label;
27
}
28
}
29
30
class TestToolBar extends ToolBar {
31
get actionBarForTest(): Pick<ActionBar, 'getWidth' | 'getAction'> {
32
return this.actionBar;
33
}
34
}
35
36
const contextMenuProvider: IContextMenuProvider = {
37
showContextMenu: () => { }
38
};
39
40
suite('ToolBar', () => {
41
const store = ensureNoDisposablesAreLeakedInTestSuite();
42
43
let container: HTMLElement;
44
45
setup(() => {
46
container = document.createElement('div');
47
container.style.width = '273px';
48
document.body.appendChild(container);
49
});
50
51
teardown(() => {
52
container.remove();
53
});
54
55
test('keeps the last primary action shrinkable when overflow is inserted', () => {
56
const widths = new Map<string, number>([
57
['workbench.action.chat.attachContext', 22],
58
['workbench.action.chat.openModePicker', 75],
59
['workbench.action.chat.openModelPicker', 271],
60
['workbench.action.chat.configureTools', 22],
61
[ToggleMenuAction.ID, 22],
62
]);
63
64
const toolbar = store.add(new TestToolBar(container, contextMenuProvider, {
65
responsiveBehavior: {
66
enabled: true,
67
kind: 'last',
68
minItems: 1,
69
actionMinWidth: 22,
70
},
71
actionViewItemProvider: action => {
72
const width = widths.get(action.id);
73
return typeof width === 'number' ? new FixedWidthActionViewItem(action, width) : undefined;
74
}
75
}));
76
const actionBar = toolbar.actionBarForTest;
77
const originalGetWidth = actionBar.getWidth.bind(actionBar);
78
actionBar.getWidth = (index: number) => {
79
const action = actionBar.getAction(index);
80
return action ? (widths.get(action.id) ?? originalGetWidth(index)) : originalGetWidth(index);
81
};
82
83
const originalGetBoundingClientRect = toolbar.getElement().getBoundingClientRect.bind(toolbar.getElement());
84
(toolbar.getElement() as HTMLElement & { getBoundingClientRect(): DOMRect }).getBoundingClientRect = () => ({
85
...originalGetBoundingClientRect(),
86
width: 273,
87
right: 273,
88
left: 0,
89
x: 0,
90
y: 0,
91
top: 0,
92
bottom: 0,
93
height: 0,
94
toJSON() {
95
return {};
96
}
97
});
98
99
const actions = [
100
store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),
101
store.add(new Action('workbench.action.chat.openModePicker', 'Open Agent Picker')),
102
store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),
103
store.add(new Action('workbench.action.chat.configureTools', 'Configure Tools...')),
104
];
105
106
toolbar.setActions(actions);
107
108
assert.strictEqual(toolbar.getItemsLength(), 4);
109
assert.strictEqual(toolbar.getItemAction(0)?.id, 'workbench.action.chat.attachContext');
110
assert.strictEqual(toolbar.getItemAction(1)?.id, 'workbench.action.chat.openModePicker');
111
assert.strictEqual(toolbar.getItemAction(2)?.id, 'workbench.action.chat.openModelPicker');
112
assert.strictEqual(toolbar.getItemAction(3)?.id, ToggleMenuAction.ID);
113
assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), true);
114
});
115
116
test('applies per-action responsive min widths', () => {
117
const toolbar = store.add(new ToolBar(container, contextMenuProvider, {
118
responsiveBehavior: {
119
enabled: true,
120
kind: 'last',
121
minItems: 1,
122
actionMinWidth: 22,
123
getActionMinWidth: action => action.id === 'workbench.action.chat.openModelPicker' ? 28 : undefined,
124
},
125
actionViewItemProvider: action => new FixedWidthActionViewItem(action, 22)
126
}));
127
128
const actions = [
129
store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),
130
store.add(new Action('workbench.action.chat.openModePicker', 'Open Agent Picker')),
131
store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),
132
];
133
134
toolbar.setActions(actions);
135
136
assert.strictEqual(toolbar.getElement().style.getPropertyValue('--vscode-toolbar-action-min-width'), '28px');
137
});
138
139
test('relayout re-evaluates responsive overflow after action width changes', () => {
140
const widths = new Map<string, number>([
141
['workbench.action.chat.attachContext', 22],
142
['workbench.action.chat.openModePicker', 22],
143
['workbench.action.chat.openModelPicker', 50],
144
[ToggleMenuAction.ID, 22],
145
]);
146
147
const toolbar = store.add(new TestToolBar(container, contextMenuProvider, {
148
responsiveBehavior: {
149
enabled: true,
150
kind: 'last',
151
minItems: 1,
152
actionMinWidth: 22,
153
},
154
actionViewItemProvider: action => {
155
const width = widths.get(action.id);
156
return typeof width === 'number' ? new FixedWidthActionViewItem(action, width) : undefined;
157
}
158
}));
159
const actionBar = toolbar.actionBarForTest;
160
const originalGetWidth = actionBar.getWidth.bind(actionBar);
161
actionBar.getWidth = (index: number) => {
162
const action = actionBar.getAction(index);
163
return action ? (widths.get(action.id) ?? originalGetWidth(index)) : originalGetWidth(index);
164
};
165
166
const originalGetBoundingClientRect = toolbar.getElement().getBoundingClientRect.bind(toolbar.getElement());
167
(toolbar.getElement() as HTMLElement & { getBoundingClientRect(): DOMRect }).getBoundingClientRect = () => ({
168
...originalGetBoundingClientRect(),
169
width: 110,
170
right: 110,
171
left: 0,
172
x: 0,
173
y: 0,
174
top: 0,
175
bottom: 0,
176
height: 0,
177
toJSON() {
178
return {};
179
}
180
});
181
182
const actions = [
183
store.add(new Action('workbench.action.chat.attachContext', 'Add Context...')),
184
store.add(new Action('workbench.action.chat.openModePicker', 'Open Mode Picker')),
185
store.add(new Action('workbench.action.chat.openModelPicker', 'Open Model Picker')),
186
];
187
188
toolbar.setActions(actions);
189
190
assert.strictEqual(toolbar.getItemsLength(), 3);
191
assert.strictEqual(toolbar.getItemAction(2)?.id, 'workbench.action.chat.openModelPicker');
192
assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), false);
193
194
widths.set('workbench.action.chat.openModePicker', 80);
195
toolbar.relayout();
196
197
assert.strictEqual(toolbar.getItemsLength(), 3);
198
assert.strictEqual(toolbar.getItemAction(0)?.id, 'workbench.action.chat.attachContext');
199
assert.strictEqual(toolbar.getItemAction(1)?.id, 'workbench.action.chat.openModePicker');
200
assert.strictEqual(toolbar.getItemAction(2)?.id, ToggleMenuAction.ID);
201
assert.strictEqual(toolbar.getElement().querySelector('.monaco-action-bar')?.classList.contains('has-overflow'), true);
202
});
203
});
204
205