Skip to content

Commit

Permalink
[fixed] corretly walk when using TAB.
Browse files Browse the repository at this point in the history
closes reactjs#511.
  • Loading branch information
diasbruno committed Nov 6, 2017
1 parent 5cf9326 commit 0f2bf9e
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 71 deletions.
2 changes: 2 additions & 0 deletions examples/basic/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -11,6 +12,7 @@ Modal.setAppElement('#example');

const examples = [
SimpleUsage,
Forms,
MultipleModals,
ReactRouter
];
Expand Down
108 changes: 45 additions & 63 deletions examples/basic/forms/index.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,78 @@
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 (
<div>
<button onClick={this.toggleModal(MODAL_A)}>Open Modal A</button>
<button onClick={this.toggleModal(MODAL_B)}>Open Modal B</button>
<MyModal
title={this.state.title1}
isOpen={currentModal == MODAL_A}
onAfterOpen={this.handleOnAfterOpenModal}
onRequestClose={this.handleModalCloseRequest}
askToClose={this.toggleModal(MODAL_A)}
onChangeInput={this.handleInputChange} />
<button className="btn btn-primary" onClick={this.toggleModal}>Open Modal</button>
<Modal
ref="mymodal2"
id="test2"
id="modal_with_forms"
isOpen={isOpen}
closeTimeoutMS={150}
contentLabel="modalB"
shouldCloseOnOverlayClick={true}
onRequestClose={this.toggleModal}
aria={{
labelledby: "heading",
describedby: "fulldescription"
}}
closeTimeoutMS={150}
contentLabel="modalB"
isOpen={currentModal == MODAL_B}
onAfterOpen={this.handleOnAfterOpenModal}
onRequestClose={this.toggleModal(MODAL_B)}>
<h1 id="heading" ref={headingRef}>This is the modal 2!</h1>
}}>
<h1 id="heading">Forms!</h1>
<div id="fulldescription" tabIndex="0" role="document">
<p>This is a description of what it does: nothing :)</p>
</div>p

<form>
<fieldset>
<input type="text" />
<input type="text" />
</fieldset>
<fieldset>
<legend>Radio buttons</legend>
<label>
<input id="radio-a" name="radios" type="radio" /> A
</label>
<label>
<input id="radio-b" name="radios" type="radio" /> B
</label>
</fieldset>
<fieldset>
<legend>Checkbox buttons</legend>
<label>
<input id="checkbox-a" name="checkbox-a" type="checkbox" /> A
</label>
<label>
<input id="checkbox-b" name="checkbox-b" type="checkbox" /> B
</label>
</fieldset>
<input type="text" />
</form>
</div>
</Modal>
</div>
);
}
}

export default {
label: "#1. Working with one modal at a time.",
app: SimpleUsage
label: "#3. Modal with forms fields.",
app: Forms
};
64 changes: 56 additions & 8 deletions src/helpers/scopeTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

0 comments on commit 0f2bf9e

Please sign in to comment.