Skip to content

Commit

Permalink
fix(Portal) portal should take focus when open and restore when closed
Browse files Browse the repository at this point in the history
  • Loading branch information
fracmak committed Jan 17, 2017
1 parent a459425 commit 8e24170
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/addons/Portal/Portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class Portal extends Component {
if (!this.state.open) return
debug('renderPortal()')

const { children, className } = this.props
const { children, className, closeOnTriggerBlur } = this.props

this.mountPortal()

Expand All @@ -363,6 +363,14 @@ class Portal extends Component {
)

this.portal = this.node.firstElementChild
// don't take focus away from portals that close on blur
if (!closeOnTriggerBlur) {
this.previousActiveElement = document.activeElement
this.portal.setAttribute('tabindex', '-1')
this.portal.style.outline = 'none'
// wait a tick for things like popups which need to calculate where the popup shows up
setTimeout(() => this.portal && this.portal.focus())
}

this.portal.addEventListener('mouseleave', this.handlePortalMouseLeave)
this.portal.addEventListener('mouseenter', this.handlePortalMouseEnter)
Expand Down Expand Up @@ -397,6 +405,7 @@ class Portal extends Component {

ReactDOM.unmountComponentAtNode(this.node)
this.node.parentNode.removeChild(this.node)
if (this.previousActiveElement) this.previousActiveElement.focus()

this.portal.removeEventListener('mouseleave', this.handlePortalMouseLeave)
this.portal.removeEventListener('mouseenter', this.handlePortalMouseEnter)
Expand Down
43 changes: 43 additions & 0 deletions test/specs/addons/Portal/Portal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,4 +509,47 @@ describe('Portal', () => {
document.body.childElementCount.should.equal(0)
})
})

describe('focus', () => {
it('should take focus when mounted', (done) => {
attachTo = document.createElement('div')
document.body.appendChild(attachTo)
const opts = { attachTo }
const portal = wrapperMount(<Portal defaultOpen><p>Hi</p></Portal>, opts)
setTimeout(() => {
const portalNode = portal.node.node.firstElementChild
expect(document.activeElement).to.equal(portalNode)
expect(portalNode.getAttribute('tabindex')).to.equal('-1')
expect(portalNode.style.outline).to.equal('none')
done()
})
})
it('should not take focus when mounted on portals that closeOnTriggerBlur', (done) => {
attachTo = document.createElement('div')
document.body.appendChild(attachTo)
const opts = { attachTo }
const portal = wrapperMount(<Portal defaultOpen closeOnTriggerBlur><p>Hi</p></Portal>, opts)
setTimeout(() => {
const portalNode = portal.node.node.firstElementChild
expect(document.activeElement).to.not.equal(portalNode)
expect(portalNode.getAttribute('tabindex')).to.not.equal('-1')
expect(portalNode.style.outline).to.not.equal('none')
done()
})
})
it('should restore focus when unmounted', (done) => {
const activeElement = document.activeElement
attachTo = document.createElement('div')
document.body.appendChild(attachTo)
const opts = { attachTo }
const portal = wrapperMount(<Portal open><p>Hi</p></Portal>, opts)
setTimeout(() => {
portal.setProps({
open: false,
})
expect(document.activeElement).to.equal(activeElement)
done()
})
})
})
})

0 comments on commit 8e24170

Please sign in to comment.