Skip to content

Commit d6971fe

Browse files
authored
feat(auth): navigate login page with keyboard (#4962)
* feat(auth): navigate login page with keyboard - Use tab/shift+tab to navigate the login. - Press enter on start url field to shortcut continue (if the form is valid). - Press enter on any of the IAM credential fields to continue (if the form is valid). - Autofocus the start url field. - Autofocus IAM profile name field. * add support for focus/enter on IAM creds * changelog items
1 parent 5d19062 commit d6971fe

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "UX: Added keyboard navigation to login screen."
4+
}

packages/core/src/login/webview/vue/login.vue

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
name="startUrl"
185185
@input="handleUrlInput"
186186
v-model="startUrl"
187+
@keydown.enter="handleContinueClick()"
187188
/>
188189
<h4 class="start-url-error">{{ startUrlError }}</h4>
189190
<div class="title topMargin">Region</div>
@@ -201,7 +202,7 @@
201202
</select>
202203
<button
203204
class="continue-button topMargin"
204-
:disabled="startUrl.length == 0 || startUrlError.length > 0 || !selectedRegion"
205+
:disabled="shouldDisableSsoContinue()"
205206
v-on:click="handleContinueClick()"
206207
>
207208
Continue
@@ -245,16 +246,27 @@
245246
id="profileName"
246247
name="profileName"
247248
v-model="profileName"
249+
@keydown.enter="handleContinueClick()"
248250
/>
249251
<div class="title">Access Key</div>
250-
<input class="iamInput bottomMargin" type="text" id="accessKey" name="accessKey" v-model="accessKey" />
252+
<input
253+
class="iamInput bottomMargin"
254+
type="text"
255+
id="accessKey"
256+
name="accessKey"
257+
v-model="accessKey"
258+
@keydown.enter="handleContinueClick()"
259+
/>
251260
<div class="title">Secret Key</div>
252-
<input class="iamInput bottomMargin" type="text" id="secretKey" name="secretKey" v-model="secretKey" />
253-
<button
254-
class="continue-button"
255-
:disabled="profileName.length <= 0 || accessKey.length <= 0 || secretKey.length <= 0"
256-
v-on:click="handleContinueClick()"
257-
>
261+
<input
262+
class="iamInput bottomMargin"
263+
type="text"
264+
id="secretKey"
265+
name="secretKey"
266+
v-model="secretKey"
267+
@keydown.enter="handleContinueClick()"
268+
/>
269+
<button class="continue-button" :disabled="shouldDisableIamContinue()" v-on:click="handleContinueClick()">
258270
Continue
259271
</button>
260272
</template>
@@ -393,7 +405,6 @@ export default defineComponent({
393405
}
394406
},
395407
async handleContinueClick() {
396-
void client.emitUiClick('auth_continueButton')
397408
if (this.stage === 'START') {
398409
if (this.selectedLoginOption === LoginOption.BUILDER_ID) {
399410
this.stage = 'AUTHENTICATING'
@@ -406,6 +417,7 @@ export default defineComponent({
406417
}
407418
} else if (this.selectedLoginOption === LoginOption.ENTERPRISE_SSO) {
408419
this.stage = 'SSO_FORM'
420+
this.$nextTick(() => document.getElementById('startUrl')!.focus())
409421
await client.storeMetricMetadata({ region: this.selectedRegion })
410422
} else if (this.selectedLoginOption >= LoginOption.EXISTING_LOGINS) {
411423
this.stage = 'AUTHENTICATING'
@@ -420,8 +432,12 @@ export default defineComponent({
420432
}
421433
} else if (this.selectedLoginOption === LoginOption.IAM_CREDENTIAL) {
422434
this.stage = 'AWS_PROFILE'
435+
this.$nextTick(() => document.getElementById('profileName')!.focus())
423436
}
424437
} else if (this.stage === 'SSO_FORM') {
438+
if (this.shouldDisableSsoContinue()) {
439+
return
440+
}
425441
this.stage = 'AUTHENTICATING'
426442
const error = await client.startEnterpriseSetup(this.startUrl, this.selectedRegion, this.app)
427443
if (error) {
@@ -431,6 +447,9 @@ export default defineComponent({
431447
this.stage = 'CONNECTED'
432448
}
433449
} else if (this.stage === 'AWS_PROFILE') {
450+
if (this.shouldDisableIamContinue()) {
451+
return
452+
}
434453
this.stage = 'AUTHENTICATING'
435454
const error = await client.startIamCredentialSetup(this.profileName, this.accessKey, this.secretKey)
436455
if (error) {
@@ -440,6 +459,7 @@ export default defineComponent({
440459
this.stage = 'CONNECTED'
441460
}
442461
}
462+
void client.emitUiClick('auth_continueButton')
443463
},
444464
async handleCodeCatalystSignin() {
445465
void client.emitUiClick('auth_codeCatalystSignIn')
@@ -525,6 +545,12 @@ export default defineComponent({
525545
handleHelpLinkClick() {
526546
void client.emitUiClick('auth_helpLink')
527547
},
548+
shouldDisableSsoContinue() {
549+
return this.startUrl.length == 0 || this.startUrlError.length > 0 || !this.selectedRegion
550+
},
551+
shouldDisableIamContinue() {
552+
return this.profileName.length <= 0 || this.accessKey.length <= 0 || this.secretKey.length <= 0
553+
},
528554
},
529555
})
530556
</script>

packages/core/src/login/webview/vue/selectableItem.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<template>
22
<div
33
class="item-container-base"
4+
tabindex="0"
45
:class="{ selected: isSelected, hovering: isHovering }"
56
@click="toggleSelection"
7+
@keydown.enter="toggleSelection"
68
@mouseover="isHovering = true"
79
@mouseout="isHovering = false"
810
>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "UX: Added keyboard navigation to login screen."
4+
}

0 commit comments

Comments
 (0)