Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 45 additions & 24 deletions packages/core/src/shared/ui/common/regionSubmenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -22,6 +24,23 @@ export class RegionSubmenu<T> extends Prompter<RegionSubmenuResponse<T>> {
private currentState: 'data' | 'region' = 'data'
private steps?: [current: number, total: number]
public activePrompter?: QuickPickPrompter<typeof switchRegion | T> | QuickPickPrompter<Region>
private readonly defaultItems: DataQuickPickItem<typeof switchRegion | T>[] = [
{
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<T>,
Expand All @@ -33,30 +52,32 @@ export class RegionSubmenu<T> extends Prompter<RegionSubmenuResponse<T>> {
super()
}

public refresh(prompter: QuickPickPrompter<T | typeof switchRegion>): 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<T | typeof switchRegion>(
this.itemsProvider(this.currentRegion),
this.dataOptions as ExtendedQuickPickOptions<T | typeof switchRegion>
)

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<T | typeof switchRegion>(items, {
...this.dataOptions,
buttons: [...(this.dataOptions?.buttons ?? []), refreshButton],
} as ExtendedQuickPickOptions<T | typeof switchRegion>)

prompter.quickPick.items = [...this.defaultItems, ...prompter.quickPick.items]

refreshButton.onClick = () => this.refresh(prompter)

return prompter
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -12,7 +13,7 @@ describe('regionSubmenu', function () {
let submenuPrompter: RegionSubmenu<string>

const region1Data = ['option1a', 'option2a', 'option3a']
const region2Data = ['option1b', 'option2b', 'option3b']
let region2Data = ['option1b', 'option2b', 'option3b']

before(async function () {
const mockDataProvider = function (regionCode: string) {
Expand Down Expand Up @@ -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<any>)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Feature",
"description": "UI: region-related quickpicks now have a refresh button"
}
Loading