Repository: microsoft/vscode (TypeScript)
Date: 2021-05-04 18:46:27+00:00
Task type: Bug Fix

Issue:
QuickPick doesn't fire the `onDidChangeValue` when setting value manually
If you set `QuickPick.value` programatically when setting up the QuickPick, it fails to trigger the `onDidChangeValue` event.

Here is my workaround hack:
https://github.com/eamodio/vscode-gitlens/blob/db5285d4ad268148dbc4a6ce9d1f284d5e18ac4e/src/commands/gitCommands.ts#L717-L727
QuickPick doesn't fire the `onDidChangeValue` when setting value manually
If you set `QuickPick.value` programatically when setting up the QuickPick, it fails to trigger the `onDidChangeValue` event.

Here is my workaround hack:
https://github.com/eamodio/vscode-gitlens/blob/db5285d4ad268148dbc4a6ce9d1f284d5e18ac4e/src/commands/gitCommands.ts#L717-L727


Issue discussion:

Solution patch:
diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts
--- a/src/vs/base/parts/quickinput/browser/quickInput.ts
+++ b/src/vs/base/parts/quickinput/browser/quickInput.ts
@@ -473,8 +473,11 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
 	}
 
 	set value(value: string) {
-		this._value = value || '';
-		this.update();
+		if (this._value !== value) {
+			this._value = value || '';
+			this.update();
+			this.onDidChangeValueEmitter.fire(this._value);
+		}
 	}
 
 	filterValue = (value: string) => value;


Test patch:
diff --git a/src/vs/base/test/parts/quickinput/browser/quickinput.test.ts b/src/vs/base/test/parts/quickinput/browser/quickinput.test.ts
new file mode 100644
--- /dev/null
+++ b/src/vs/base/test/parts/quickinput/browser/quickinput.test.ts
@@ -0,0 +1,75 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
+import { List } from 'vs/base/browser/ui/list/listWidget';
+import { QuickInputController } from 'vs/base/parts/quickinput/browser/quickInput';
+import { IWorkbenchListOptions } from 'vs/platform/list/browser/listService';
+
+// Simple promisify of setTimeout
+function wait(delayMS: number) {
+	return new Promise(function (resolve) {
+		setTimeout(resolve, delayMS);
+	});
+}
+
+suite('QuickInput', () => {
+	let fixture: HTMLElement, controller: QuickInputController;
+
+	setup(() => {
+		fixture = document.createElement('div');
+		document.body.appendChild(fixture);
+
+		controller = new QuickInputController({
+			container: fixture,
+			idPrefix: 'testQuickInput',
+			ignoreFocusOut() { return false; },
+			isScreenReaderOptimized() { return false; },
+			returnFocus() { },
+			backKeybindingLabel() { return undefined; },
+			setContextKey() { return undefined; },
+			createList: <T>(
+				user: string,
+				container: HTMLElement,
+				delegate: IListVirtualDelegate<T>,
+				renderers: IListRenderer<T, any>[],
+				options: IWorkbenchListOptions<T>,
+			) => new List<T>(user, container, delegate, renderers, options),
+			styles: {
+				button: {},
+				countBadge: {},
+				inputBox: {},
+				keybindingLabel: {},
+				list: {},
+				progressBar: {},
+				widget: {}
+			}
+		});
+	});
+
+	teardown(() => {
+		controller.dispose();
+		document.body.removeChild(fixture);
+	});
+
+	test('onDidChangeValue gets triggered when .value is set', async () => {
+		const quickpick = controller.createQuickPick();
+
+		let value: string | undefined = undefined;
+		quickpick.onDidChangeValue((e) => value = e);
+
+		// Trigger a change
+		quickpick.value = 'changed';
+
+		try {
+			// wait a bit to let the event play out.
+			await wait(200);
+			assert.strictEqual(value, quickpick.value);
+		} finally {
+			quickpick.dispose();
+		}
+	});
+});