mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-12 22:50:54 +03:00
Merge pull request #15163 from atom/fb-wb-modal-focus
Automatically manage focus for modal panels
This commit is contained in:
commit
96b3af143d
@ -32,6 +32,7 @@
|
||||
"event-kit": "^2.3.0",
|
||||
"find-parent-dir": "^0.3.0",
|
||||
"first-mate": "7.0.7",
|
||||
"focus-trap": "^2.3.0",
|
||||
"fs-admin": "^0.1.5",
|
||||
"fs-plus": "^3.0.1",
|
||||
"fstream": "0.1.24",
|
||||
|
@ -17,6 +17,7 @@ describe('PanelContainerElement', () => {
|
||||
this.model = model
|
||||
return this
|
||||
}
|
||||
focus() {}
|
||||
}
|
||||
|
||||
const TestPanelContainerItemElement = document.registerElement(
|
||||
@ -159,5 +160,57 @@ describe('PanelContainerElement', () => {
|
||||
expect(panel1.getElement()).toHaveClass('overlay')
|
||||
expect(panel1.getElement()).toHaveClass('from-top')
|
||||
})
|
||||
|
||||
describe("autoFocus", () => {
|
||||
function createPanel() {
|
||||
const panel = new Panel(
|
||||
{
|
||||
item: new TestPanelContainerItem(),
|
||||
autoFocus: true,
|
||||
visible: false
|
||||
},
|
||||
atom.views
|
||||
)
|
||||
|
||||
container.addPanel(panel)
|
||||
return panel
|
||||
}
|
||||
|
||||
it("focuses the first tabbable item if available", () => {
|
||||
const panel = createPanel()
|
||||
const panelEl = panel.getElement()
|
||||
const inputEl = document.createElement('input')
|
||||
|
||||
panelEl.appendChild(inputEl)
|
||||
expect(document.activeElement).not.toBe(inputEl)
|
||||
|
||||
panel.show()
|
||||
expect(document.activeElement).toBe(inputEl)
|
||||
})
|
||||
|
||||
it("focuses the entire panel item when no tabbable item is available and the panel is focusable", () => {
|
||||
const panel = createPanel()
|
||||
const panelEl = panel.getElement()
|
||||
|
||||
spyOn(panelEl, 'focus')
|
||||
panel.show()
|
||||
expect(panelEl.focus).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("returns focus to the original activeElement", () => {
|
||||
const panel = createPanel()
|
||||
const previousActiveElement = document.activeElement
|
||||
const panelEl = panel.getElement()
|
||||
panelEl.appendChild(document.createElement('input'))
|
||||
|
||||
panel.show()
|
||||
panel.hide()
|
||||
|
||||
waitsFor(() => document.activeElement === previousActiveElement)
|
||||
runs(() => {
|
||||
expect(document.activeElement).toBe(previousActiveElement)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const focusTrap = require('focus-trap')
|
||||
const {CompositeDisposable} = require('event-kit')
|
||||
|
||||
class PanelContainerElement extends HTMLElement {
|
||||
@ -52,6 +53,26 @@ class PanelContainerElement extends HTMLElement {
|
||||
this.subscriptions.add(panel.onDidChangeVisible(visible => {
|
||||
if (visible) { this.hideAllPanelsExcept(panel) }
|
||||
}))
|
||||
|
||||
if (panel.autoFocus) {
|
||||
const modalFocusTrap = focusTrap(panelElement, {
|
||||
// focus-trap will attempt to give focus to the first tabbable element
|
||||
// on activation. If there aren't any tabbable elements,
|
||||
// give focus to the panel element itself
|
||||
fallbackFocus: panelElement,
|
||||
// closing is handled by core Atom commands and this already deactivates
|
||||
// on visibility changes
|
||||
escapeDeactivates: false
|
||||
})
|
||||
|
||||
this.subscriptions.add(panel.onDidChangeVisible(visible => {
|
||||
if (visible) {
|
||||
modalFocusTrap.activate()
|
||||
} else {
|
||||
modalFocusTrap.deactivate()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,16 +13,15 @@ class Panel {
|
||||
Section: Construction and Destruction
|
||||
*/
|
||||
|
||||
constructor ({item, visible, priority, className}, viewRegistry) {
|
||||
constructor ({item, autoFocus, visible, priority, className}, viewRegistry) {
|
||||
this.destroyed = false
|
||||
this.item = item
|
||||
this.visible = visible
|
||||
this.priority = priority
|
||||
this.autoFocus = autoFocus == null ? false : autoFocus
|
||||
this.visible = visible == null ? true : visible
|
||||
this.priority = priority == null ? 100 : priority
|
||||
this.className = className
|
||||
this.viewRegistry = viewRegistry
|
||||
this.emitter = new Emitter()
|
||||
if (this.visible == null) this.visible = true
|
||||
if (this.priority == null) this.priority = 100
|
||||
}
|
||||
|
||||
// Public: Destroy and remove this panel from the UI.
|
||||
|
@ -1752,6 +1752,11 @@ module.exports = class Workspace extends Model {
|
||||
// (default: true)
|
||||
// * `priority` (optional) {Number} Determines stacking order. Lower priority items are
|
||||
// forced closer to the edges of the window. (default: 100)
|
||||
// * `autoFocus` (optional) {Boolean} true if you want modal focus managed for you by Atom.
|
||||
// Atom will automatically focus your modal panel's first tabbable element when the modal
|
||||
// opens and will restore the previously selected element when the modal closes. Atom will
|
||||
// also automatically restrict user tab focus within your modal while it is open.
|
||||
// (default: false)
|
||||
//
|
||||
// Returns a {Panel}
|
||||
addModalPanel (options = {}) {
|
||||
|
Loading…
Reference in New Issue
Block a user