diff --git a/packages/core/src/shared/ui/common/regionSubmenu.ts b/packages/core/src/shared/ui/common/regionSubmenu.ts index fde9f0545e4..c8c054c57ea 100644 --- a/packages/core/src/shared/ui/common/regionSubmenu.ts +++ b/packages/core/src/shared/ui/common/regionSubmenu.ts @@ -5,11 +5,13 @@ import * as vscode from 'vscode' import globals from '../../extensionGlobals' import { isValidResponse, StepEstimator } from '../../wizards/wizard' -import { createQuickPick, ExtendedQuickPickOptions, ItemLoadTypes } from '../pickerPrompter' +import { createQuickPick, DataQuickPickItem, ExtendedQuickPickOptions, ItemLoadTypes } from '../pickerPrompter' import { Prompter, PromptResult } from '../prompter' import { createRegionPrompter } from './region' import { QuickPickPrompter } from '../pickerPrompter' import { Region } from '../../regions/endpoints' +import { createRefreshButton } from '../buttons' +import { getLogger } from '../../logger' const switchRegion = Symbol('switchRegion') @@ -22,6 +24,23 @@ export class RegionSubmenu extends Prompter> { private currentState: 'data' | 'region' = 'data' private steps?: [current: number, total: number] public activePrompter?: QuickPickPrompter | QuickPickPrompter + private readonly defaultItems: DataQuickPickItem[] = [ + { + label: 'Actions', + kind: vscode.QuickPickItemKind.Separator, + data: undefined, + }, + { + label: 'Switch Region', + data: switchRegion, + description: `current region: ${this.currentRegion}`, + }, + { + label: this.separatorLabel, + kind: vscode.QuickPickItemKind.Separator, + data: undefined, + }, + ] public constructor( private readonly itemsProvider: (region: string) => ItemLoadTypes, @@ -33,30 +52,32 @@ export class RegionSubmenu extends Prompter> { super() } + public refresh(prompter: QuickPickPrompter): void { + // This method cannot be async due to onClick() specifications. Thus we are forced to use .then, .catch as workaround. + const activeBefore = prompter.quickPick.activeItems + prompter + .clearAndLoadItems(this.itemsProvider(this.currentRegion)) + .then(() => { + prompter.quickPick.items = [...this.defaultItems, ...prompter.quickPick.items] + prompter.quickPick.activeItems = activeBefore + }) + .catch((e) => { + getLogger().error('clearAndLoadItems failed: %s', (e as Error).message) + }) + } + private createMenuPrompter() { - const prompter = createQuickPick( - this.itemsProvider(this.currentRegion), - this.dataOptions as ExtendedQuickPickOptions - ) - - prompter.quickPick.items = [ - { - label: 'Actions', - kind: vscode.QuickPickItemKind.Separator, - data: undefined, - }, - { - label: 'Switch Region', - data: switchRegion, - description: `current region: ${this.currentRegion}`, - }, - { - label: this.separatorLabel, - kind: vscode.QuickPickItemKind.Separator, - data: undefined, - }, - ...prompter.quickPick.items, - ] + const refreshButton = createRefreshButton() + const items = this.itemsProvider(this.currentRegion) + const prompter = createQuickPick(items, { + ...this.dataOptions, + buttons: [...(this.dataOptions?.buttons ?? []), refreshButton], + } as ExtendedQuickPickOptions) + + prompter.quickPick.items = [...this.defaultItems, ...prompter.quickPick.items] + + refreshButton.onClick = () => this.refresh(prompter) + return prompter } diff --git a/packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts b/packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts index c0b2613d268..10b9ceb1d90 100644 --- a/packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts +++ b/packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts @@ -4,6 +4,7 @@ */ import assert from 'assert' +import * as sinon from 'sinon' import { RegionSubmenu } from '../../../../shared/ui/common/regionSubmenu' import { DataQuickPickItem, QuickPickPrompter } from '../../../../shared/ui/pickerPrompter' import { createQuickPickPrompterTester } from '../../../shared/ui/testUtils' @@ -12,7 +13,7 @@ describe('regionSubmenu', function () { let submenuPrompter: RegionSubmenu const region1Data = ['option1a', 'option2a', 'option3a'] - const region2Data = ['option1b', 'option2b', 'option3b'] + let region2Data = ['option1b', 'option2b', 'option3b'] before(async function () { const mockDataProvider = function (regionCode: string) { @@ -64,4 +65,34 @@ describe('regionSubmenu', function () { data: 'option2b', }) }) + + it('only has a refresh button', function () { + const activeButtons = submenuPrompter.activePrompter!.quickPick.buttons + assert.strictEqual(activeButtons.length, 1) + }) + + it('refresh button calls refresh once onClick', function () { + const refreshButton = submenuPrompter.activePrompter!.quickPick.buttons[0] + const refreshStub = sinon.stub(RegionSubmenu.prototype, 'refresh') + refreshButton.onClick!() + sinon.assert.calledOnce(refreshStub) + sinon.restore() + }) + + it('refresh reloads items', async function () { + const itemsBeforeLabels = submenuPrompter.activePrompter!.quickPick.items.map((i) => i.label) + region2Data = ['option1c', 'option2c', 'option3c'] + + // Note that onDidChangeBusy event fires with busy=false when we load new items in. + // Since regionSubmenu retroactively adds the default items, they won't be there yet. + // So we don't check for them in test to avoid looking at implementation level details. + submenuPrompter.activePrompter!.onDidChangeBusy((b: boolean) => { + if (!b) { + const itemsAfterLabels = submenuPrompter.activePrompter!.quickPick.items.map((i) => i.label) + region2Data.forEach((item) => assert(itemsAfterLabels.includes(item))) + assert.notStrictEqual(itemsBeforeLabels, itemsAfterLabels) + } + }) + submenuPrompter.refresh(submenuPrompter.activePrompter! as QuickPickPrompter) + }) }) diff --git a/packages/toolkit/.changes/next-release/Feature-8f11662c-6a2c-45cf-82e4-bb85e0b3adb0.json b/packages/toolkit/.changes/next-release/Feature-8f11662c-6a2c-45cf-82e4-bb85e0b3adb0.json new file mode 100644 index 00000000000..8c51547a599 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-8f11662c-6a2c-45cf-82e4-bb85e0b3adb0.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "UI: region-related quickpicks now have a refresh button" +}