Skip to content

Commit acfdb61

Browse files
authored
feat(ui): add refresh button to regionSubmenu (#5641)
## Problem Originally, wanted refresh for EC2 connect QuickPick so that we can update instance status in the QuickPick. This problem can be generalized to all users of the regionSubmenu. ## Solution Add refresh button to regionSubmenu. ### Implementation Details - Testing the refresh is a little awkward. There is a comment in the `packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts` file explaining the choices made. --- <!--- REMINDER: Ensure that your PR meets the guidelines in CONTRIBUTING.md --> License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 8113288 commit acfdb61

File tree

3 files changed

+81
-25
lines changed

3 files changed

+81
-25
lines changed

packages/core/src/shared/ui/common/regionSubmenu.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import * as vscode from 'vscode'
66
import globals from '../../extensionGlobals'
77
import { isValidResponse, StepEstimator } from '../../wizards/wizard'
8-
import { createQuickPick, ExtendedQuickPickOptions, ItemLoadTypes } from '../pickerPrompter'
8+
import { createQuickPick, DataQuickPickItem, ExtendedQuickPickOptions, ItemLoadTypes } from '../pickerPrompter'
99
import { Prompter, PromptResult } from '../prompter'
1010
import { createRegionPrompter } from './region'
1111
import { QuickPickPrompter } from '../pickerPrompter'
1212
import { Region } from '../../regions/endpoints'
13+
import { createRefreshButton } from '../buttons'
14+
import { getLogger } from '../../logger'
1315

1416
const switchRegion = Symbol('switchRegion')
1517

@@ -22,6 +24,23 @@ export class RegionSubmenu<T> extends Prompter<RegionSubmenuResponse<T>> {
2224
private currentState: 'data' | 'region' = 'data'
2325
private steps?: [current: number, total: number]
2426
public activePrompter?: QuickPickPrompter<typeof switchRegion | T> | QuickPickPrompter<Region>
27+
private readonly defaultItems: DataQuickPickItem<typeof switchRegion | T>[] = [
28+
{
29+
label: 'Actions',
30+
kind: vscode.QuickPickItemKind.Separator,
31+
data: undefined,
32+
},
33+
{
34+
label: 'Switch Region',
35+
data: switchRegion,
36+
description: `current region: ${this.currentRegion}`,
37+
},
38+
{
39+
label: this.separatorLabel,
40+
kind: vscode.QuickPickItemKind.Separator,
41+
data: undefined,
42+
},
43+
]
2544

2645
public constructor(
2746
private readonly itemsProvider: (region: string) => ItemLoadTypes<T>,
@@ -33,30 +52,32 @@ export class RegionSubmenu<T> extends Prompter<RegionSubmenuResponse<T>> {
3352
super()
3453
}
3554

55+
public refresh(prompter: QuickPickPrompter<T | typeof switchRegion>): void {
56+
// This method cannot be async due to onClick() specifications. Thus we are forced to use .then, .catch as workaround.
57+
const activeBefore = prompter.quickPick.activeItems
58+
prompter
59+
.clearAndLoadItems(this.itemsProvider(this.currentRegion))
60+
.then(() => {
61+
prompter.quickPick.items = [...this.defaultItems, ...prompter.quickPick.items]
62+
prompter.quickPick.activeItems = activeBefore
63+
})
64+
.catch((e) => {
65+
getLogger().error('clearAndLoadItems failed: %s', (e as Error).message)
66+
})
67+
}
68+
3669
private createMenuPrompter() {
37-
const prompter = createQuickPick<T | typeof switchRegion>(
38-
this.itemsProvider(this.currentRegion),
39-
this.dataOptions as ExtendedQuickPickOptions<T | typeof switchRegion>
40-
)
41-
42-
prompter.quickPick.items = [
43-
{
44-
label: 'Actions',
45-
kind: vscode.QuickPickItemKind.Separator,
46-
data: undefined,
47-
},
48-
{
49-
label: 'Switch Region',
50-
data: switchRegion,
51-
description: `current region: ${this.currentRegion}`,
52-
},
53-
{
54-
label: this.separatorLabel,
55-
kind: vscode.QuickPickItemKind.Separator,
56-
data: undefined,
57-
},
58-
...prompter.quickPick.items,
59-
]
70+
const refreshButton = createRefreshButton()
71+
const items = this.itemsProvider(this.currentRegion)
72+
const prompter = createQuickPick<T | typeof switchRegion>(items, {
73+
...this.dataOptions,
74+
buttons: [...(this.dataOptions?.buttons ?? []), refreshButton],
75+
} as ExtendedQuickPickOptions<T | typeof switchRegion>)
76+
77+
prompter.quickPick.items = [...this.defaultItems, ...prompter.quickPick.items]
78+
79+
refreshButton.onClick = () => this.refresh(prompter)
80+
6081
return prompter
6182
}
6283

packages/core/src/test/shared/ui/prompters/regionSubmenu.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import assert from 'assert'
7+
import * as sinon from 'sinon'
78
import { RegionSubmenu } from '../../../../shared/ui/common/regionSubmenu'
89
import { DataQuickPickItem, QuickPickPrompter } from '../../../../shared/ui/pickerPrompter'
910
import { createQuickPickPrompterTester } from '../../../shared/ui/testUtils'
@@ -12,7 +13,7 @@ describe('regionSubmenu', function () {
1213
let submenuPrompter: RegionSubmenu<string>
1314

1415
const region1Data = ['option1a', 'option2a', 'option3a']
15-
const region2Data = ['option1b', 'option2b', 'option3b']
16+
let region2Data = ['option1b', 'option2b', 'option3b']
1617

1718
before(async function () {
1819
const mockDataProvider = function (regionCode: string) {
@@ -64,4 +65,34 @@ describe('regionSubmenu', function () {
6465
data: 'option2b',
6566
})
6667
})
68+
69+
it('only has a refresh button', function () {
70+
const activeButtons = submenuPrompter.activePrompter!.quickPick.buttons
71+
assert.strictEqual(activeButtons.length, 1)
72+
})
73+
74+
it('refresh button calls refresh once onClick', function () {
75+
const refreshButton = submenuPrompter.activePrompter!.quickPick.buttons[0]
76+
const refreshStub = sinon.stub(RegionSubmenu.prototype, 'refresh')
77+
refreshButton.onClick!()
78+
sinon.assert.calledOnce(refreshStub)
79+
sinon.restore()
80+
})
81+
82+
it('refresh reloads items', async function () {
83+
const itemsBeforeLabels = submenuPrompter.activePrompter!.quickPick.items.map((i) => i.label)
84+
region2Data = ['option1c', 'option2c', 'option3c']
85+
86+
// Note that onDidChangeBusy event fires with busy=false when we load new items in.
87+
// Since regionSubmenu retroactively adds the default items, they won't be there yet.
88+
// So we don't check for them in test to avoid looking at implementation level details.
89+
submenuPrompter.activePrompter!.onDidChangeBusy((b: boolean) => {
90+
if (!b) {
91+
const itemsAfterLabels = submenuPrompter.activePrompter!.quickPick.items.map((i) => i.label)
92+
region2Data.forEach((item) => assert(itemsAfterLabels.includes(item)))
93+
assert.notStrictEqual(itemsBeforeLabels, itemsAfterLabels)
94+
}
95+
})
96+
submenuPrompter.refresh(submenuPrompter.activePrompter! as QuickPickPrompter<any>)
97+
})
6798
})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "UI: region-related quickpicks now have a refresh button"
4+
}

0 commit comments

Comments
 (0)