From 0f2bf9e92e09fe79e0b28c9f02c1ef4b4c4a19b9 Mon Sep 17 00:00:00 2001 From: Bruno Dias Date: Thu, 5 Oct 2017 22:46:34 -0300 Subject: [PATCH] [fixed] corretly walk when using TAB. closes #511. --- examples/basic/app.js | 2 + examples/basic/forms/index.js | 108 ++++++++++++++-------------------- src/helpers/scopeTab.js | 64 +++++++++++++++++--- 3 files changed, 103 insertions(+), 71 deletions(-) diff --git a/examples/basic/app.js b/examples/basic/app.js index 51e64b32..58118a47 100644 --- a/examples/basic/app.js +++ b/examples/basic/app.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'; import Modal from 'react-modal'; import SimpleUsage from './simple_usage'; import MultipleModals from './multiple_modals'; +import Forms from './forms'; import ReactRouter from './react-router'; const appElement = document.getElementById('example'); @@ -11,6 +12,7 @@ Modal.setAppElement('#example'); const examples = [ SimpleUsage, + Forms, MultipleModals, ReactRouter ]; diff --git a/examples/basic/forms/index.js b/examples/basic/forms/index.js index 5e7800bf..550194ec 100644 --- a/examples/basic/forms/index.js +++ b/examples/basic/forms/index.js @@ -1,89 +1,71 @@ import React, { Component } from 'react'; import Modal from 'react-modal'; -import MyModal from './modal'; const MODAL_A = 'modal_a'; const MODAL_B = 'modal_b'; const DEFAULT_TITLE = 'Default title'; -class SimpleUsage extends Component { +class Forms extends Component { constructor(props) { super(props); - this.state = { - title1: DEFAULT_TITLE, - currentModal: null - }; - } - - toggleModal = key => event => { - event.preventDefault(); - if (this.state.currentModal) { - this.handleModalCloseRequest(); - return; - } - - this.setState({ - ...this.state, - currentModal: key, - title1: DEFAULT_TITLE - }); - } - - handleModalCloseRequest = () => { - // opportunity to validate something and keep the modal open even if it - // requested to be closed - this.setState({ - ...this.state, - currentModal: null - }); - } - handleInputChange = e => { - let text = e.target.value; - if (text == '') { - text = DEFAULT_TITLE; - } - this.setState({ ...this.state, title1: text }); + this.state = { isOpen: false }; } - handleOnAfterOpenModal = () => { - // when ready, we can access the available refs. - this.heading && (this.heading.style.color = '#F00'); + toggleModal = event => { + console.log(event); + const { isOpen } = this.state; + this.setState({ isOpen: !isOpen }); } - headingRef = h1 => this.heading = h1; - render() { - const { currentModal } = this.state; + const { isOpen } = this.state; return (
- - - + -

This is the modal 2!

+ }}> +

Forms!

This is a description of what it does: nothing :)

-
p + +
+
+ + +
+
+ Radio buttons + + +
+
+ Checkbox buttons + + +
+ +
+
); @@ -91,6 +73,6 @@ class SimpleUsage extends Component { } export default { - label: "#1. Working with one modal at a time.", - app: SimpleUsage + label: "#3. Modal with forms fields.", + app: Forms }; diff --git a/src/helpers/scopeTab.js b/src/helpers/scopeTab.js index 53a86fc0..cf7c817d 100644 --- a/src/helpers/scopeTab.js +++ b/src/helpers/scopeTab.js @@ -2,17 +2,65 @@ import findTabbable from "./tabbable"; export default function scopeTab(node, event) { const tabbable = findTabbable(node); + if (!tabbable.length) { + // Do nothing, since there are no elements that can receive focus. + event.preventDefault(); + return; + } + + const shiftKey = event.shiftKey; + const head = tabbable[0]; + const tail = tabbable[tabbable.length - 1]; + + // proceed with default browser behavior + if (node === document.activeElement) { + return; + } + + var target; + if (tail === document.activeElement && !shiftKey) { + target = head; + } + + if (head === document.activeElement && shiftKey) { + target = tail; + } + + if (target) { event.preventDefault(); + target.focus(); return; } - const finalTabbable = tabbable[event.shiftKey ? 0 : tabbable.length - 1]; - const leavingFinalTabbable = - finalTabbable === document.activeElement || - // handle immediate shift+tab after opening with mouse - node === document.activeElement; - if (!leavingFinalTabbable) return; + + // Safari radio issue. + // + // Safari does not move the focus to the radio button, + // so we need to force it to really walk through all elements. + // + // This is very error prune, since we are trying to guess + // if it is a safari browser from the first occurence between + // chrome or safari. + // + // The chrome user agent contains the first ocurrence + // as the 'chrome/version' and later the 'safari/version'. + const checkSafari = /(\bChrome\b|\bSafari\b)\//.exec(navigator.userAgent); + const isSafariDesktop = + checkSafari != null && + checkSafari[1] != "Chrome" && + /\biPod\b|\biPad\b/g.exec(navigator.userAgent) == null; + + // If we are not in safari desktop, let the browser control + // the focus + if (!isSafariDesktop) return; + + var x = tabbable.indexOf(document.activeElement); + + if (x > -1) { + x += shiftKey ? -1 : 1; + } + event.preventDefault(); - const target = tabbable[event.shiftKey ? tabbable.length - 1 : 0]; - target.focus(); + + tabbable[x].focus(); }