diff --git a/openpilot/starpilot/galaxy/tests/test_starport_guided.mjs b/openpilot/starpilot/galaxy/tests/test_starport_guided.mjs
index 14de96aff0..77240d19c1 100644
--- a/openpilot/starpilot/galaxy/tests/test_starport_guided.mjs
+++ b/openpilot/starpilot/galaxy/tests/test_starport_guided.mjs
@@ -213,3 +213,27 @@ console.log("StarPort guided setup: Bluetooth-first hint passed")
assert.match(StarPortGuidedSetup.template, //)
}
console.log("StarPort guided setup: steps, adapter and app choices, reopening and classic-only dropdowns passed")
+
+// Choosing a cable or an app finishes the setup at once; a choice made in the card must not close the card.
+{
+ const calls = []
+ const vm = { showSteps: false, carTypeLabels: { app: "App", usb: "Wired AA" },
+ saveCarType: (index, label) => calls.push(["type", label]), saveAdapter: (index, label) => calls.push(["adapter", label]),
+ saveApp: (index, label) => calls.push(["app", label]) }
+ StarPortPage.methods.chooseType.call(vm, "app")
+ assert.equal(vm.showSteps, true)
+ vm.showSteps = false
+ StarPortPage.methods.chooseApp.call(vm, "headunitpad")
+ assert.equal(vm.showSteps, true)
+ vm.showSteps = false
+ StarPortPage.methods.chooseAdapter.call(vm, "aawireless")
+ assert.equal(vm.showSteps, true)
+ assert.deepEqual(calls, [["type", "App"], ["app", "HeadunitPad"], ["adapter", "AAWireless"]])
+ // With the card held open by a choice, the finished setup is shown for editing with its Done button, not hidden.
+ const held = guidedState({ setup, runtime: { running: false, state: "idle", connection: "headunitpad" }, setupDone: true, editing: true, carType: "app", appModels, app: "HeadunitPad" })
+ assert.equal(held.show, true)
+ assert.equal(held.editing, true)
+ assert.equal(held.choices.find((choice) => choice.selected).key, "app")
+ assert.equal(held.apps.options.find((option) => option.selected).key, "headunitpad")
+}
+console.log("StarPort guided setup: a choice keeps the card open passed")
diff --git a/openpilot/starpilot/galaxy/web/js/starport.js b/openpilot/starpilot/galaxy/web/js/starport.js
index 86c368ff77..ca5cdd9667 100644
--- a/openpilot/starpilot/galaxy/web/js/starport.js
+++ b/openpilot/starpilot/galaxy/web/js/starport.js
@@ -772,9 +772,11 @@ export const StarPortPage = {
return stopping ? stopping.then((stopped) => stopped && next()) : next()
},
useCar(address) { return this.afterWaiting(() => this.control("select_receiver", { address })) },
- chooseType(key) { return this.saveCarType(0, this.carTypeLabels[key]) },
- chooseAdapter(key) { return this.saveAdapter(0, ADAPTER_MODELS[key]) },
- chooseApp(key) { return this.saveApp(0, HEAD_UNIT_APPS[key]) },
+ // Choosing a cable or an app makes the setup count as done at once, which would close the card under the person's finger.
+ // A choice made in the card keeps it open (its Done button closes it), so the choice can still be changed.
+ chooseType(key) { this.showSteps = true; return this.saveCarType(0, this.carTypeLabels[key]) },
+ chooseAdapter(key) { this.showSteps = true; return this.saveAdapter(0, ADAPTER_MODELS[key]) },
+ chooseApp(key) { this.showSteps = true; return this.saveApp(0, HEAD_UNIT_APPS[key]) },
useClassic() { return this.feed.setNewSetup(false) },
useGuided() { return this.feed.setNewSetup(true) },
saveCarType(index, value) {
@@ -1046,7 +1048,7 @@ export const StarPortPage = {
Customize Layout
+
{{ showSteps ? 'Hide Setup' : newSetup ? 'Change how your car connects' : 'Setup and Pairing' }}