diff --git a/src/sql/workbench/browser/modal/modal.ts b/src/sql/workbench/browser/modal/modal.ts index 9dc55fad4e..5f6e61284d 100644 --- a/src/sql/workbench/browser/modal/modal.ts +++ b/src/sql/workbench/browser/modal/modal.ts @@ -84,9 +84,8 @@ export abstract class Modal extends Disposable implements IThemable { private _messageDetailText: string; private _spinnerElement: HTMLElement; - private _focusableElements: NodeListOf; - private _firstFocusableElement: HTMLElement; - private _lastFocusableElement: HTMLElement; + private _firstTabbableElement: HTMLElement; // The first element in the dialog the user could tab to + private _lastTabbableElement: HTMLElement; // The last element in the dialog the user could tab to private _focusedElementBeforeOpen: HTMLElement; private _dialogForeground?: Color; @@ -269,16 +268,18 @@ export abstract class Modal extends Disposable implements IThemable { } private handleBackwardTab(e: KeyboardEvent) { - if (this._firstFocusableElement && this._lastFocusableElement && document.activeElement === this._firstFocusableElement) { + this.setFirstLastTabbableElement(); // called every time to get the current elements + if (this._firstTabbableElement && this._lastTabbableElement && document.activeElement === this._firstTabbableElement) { e.preventDefault(); - this._lastFocusableElement.focus(); + this._lastTabbableElement.focus(); } } private handleForwardTab(e: KeyboardEvent) { - if (this._firstFocusableElement && this._lastFocusableElement && document.activeElement === this._lastFocusableElement) { + this.setFirstLastTabbableElement(); // called everytime to get the current elements + if (this._firstTabbableElement && this._lastTabbableElement && document.activeElement === this._lastTabbableElement) { e.preventDefault(); - this._firstFocusableElement.focus(); + this._firstTabbableElement.focus(); } } @@ -316,29 +317,30 @@ export abstract class Modal extends Disposable implements IThemable { } /** - * Set focusable elements in the modal dialog + * Figures out the first and last elements which the user can tab to in the dialog */ - public setFocusableElements() { - // try to find focusable element in dialog pane rather than overall container - this._focusableElements = this._modalBodySection ? - this._modalBodySection.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]') : - this._bodyContainer.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'); - if (this._focusableElements && this._focusableElements.length > 0) { - this._firstFocusableElement = this._focusableElements[0]; - this._lastFocusableElement = this._focusableElements[this._focusableElements.length - 1]; + public setFirstLastTabbableElement() { + let tabbableElements = this._bodyContainer.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'); + if (tabbableElements && tabbableElements.length > 0) { + this._firstTabbableElement = tabbableElements[0]; + this._lastTabbableElement = tabbableElements[tabbableElements.length - 1]; } - - this._focusedElementBeforeOpen = document.activeElement; - this.focus(); } /** - * Focuses the modal - * Default behavior: focus the first focusable element + * Set focusable elements in the modal dialog */ - protected focus() { - if (this._firstFocusableElement) { - this._firstFocusableElement.focus(); + public setInitialFocusedElement() { + // Try to find focusable element in dialog pane rather than overall container. _modalBodySection contains items in the pane for a wizard. + // This ensures that we are setting the focus on a useful element in the form when possible. + let focusableElements = this._modalBodySection ? + this._modalBodySection.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]') : + this._bodyContainer.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'); + + this._focusedElementBeforeOpen = document.activeElement; + + if (focusableElements && focusableElements.length > 0) { + (focusableElements[0]).focus(); } } @@ -348,7 +350,7 @@ export abstract class Modal extends Disposable implements IThemable { protected show() { this._modalShowingContext.get()!.push(this._staticKey); DOM.append(this.layoutService.container, this._bodyContainer); - this.setFocusableElements(); + this.setInitialFocusedElement(); this._keydownListener = DOM.addDisposableListener(document, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { let context = this._modalShowingContext.get()!; diff --git a/src/sql/workbench/services/backup/browser/backupUiService.ts b/src/sql/workbench/services/backup/browser/backupUiService.ts index 2f752a7759..1d26ba155c 100644 --- a/src/sql/workbench/services/backup/browser/backupUiService.ts +++ b/src/sql/workbench/services/backup/browser/backupUiService.ts @@ -108,7 +108,7 @@ export class BackupUiService implements IBackupUiService { public onShowBackupDialog() { let backupDialog = this._backupDialogs[this._currentProvider]; if (backupDialog) { - backupDialog.setFocusableElements(); + backupDialog.setInitialFocusedElement(); } }