Skip to content

Commit

Permalink
[fixed] stop propagating ESC key event.
Browse files Browse the repository at this point in the history
This is now required when nesting modals (the event will trigger
both handleKeyDown).

closes reactjs#583.
  • Loading branch information
diasbruno committed Dec 19, 2017
1 parent 6fc445e commit 9a3542a
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 8 deletions.
6 changes: 4 additions & 2 deletions examples/basic/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SimpleUsage from './simple_usage';
import MultipleModals from './multiple_modals';
import Forms from './forms';
import ReactRouter from './react-router';
import NestedModals from './nested_modals';

const appElement = document.getElementById('example');

Expand All @@ -14,6 +15,7 @@ const examples = [
SimpleUsage,
Forms,
MultipleModals,
NestedModals,
ReactRouter
];

Expand All @@ -24,8 +26,8 @@ class App extends Component {
{examples.map((example, key) => {
const ExampleApp = example.app;
return (
<div key={key} className="example">
<h3>{example.label}</h3>
<div key={key + 1} className="example">
<h3>{`#${key + 1}. ${example.label}`}</h3>
<ExampleApp />
</div>
);
Expand Down
3 changes: 1 addition & 2 deletions examples/basic/forms/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class Forms extends Component {
<h1 id="heading">Forms!</h1>
<div id="fulldescription" tabIndex="0" role="document">
<p>This is a description of what it does: nothing :)</p>

<form>
<fieldset>
<input type="text" />
Expand Down Expand Up @@ -73,6 +72,6 @@ class Forms extends Component {
}

export default {
label: "#3. Modal with forms fields.",
label: "Modal with forms fields.",
app: Forms
};
2 changes: 1 addition & 1 deletion examples/basic/multiple_modals/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,6 @@ class MultipleModals extends Component {
}

export default {
label: "#2. Working with many modal.",
label: "Working with many modal.",
app: MultipleModals
};
117 changes: 117 additions & 0 deletions examples/basic/nested_modals/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, { Component } from 'react';
import Modal from 'react-modal';

class Item extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
}

toggleModal = index => event => {
console.log("NESTED MODAL ITEM", event);
this.setState({
itemNumber: !this.state.isOpen ? index : null,
isOpen: !this.state.isOpen
});
};

render() {
const { isOpen, itemNumber } = this.state;
const { number, index } = this.props;

const toggleModal = this.toggleModal(index);

return (
<div key={index} onClick={toggleModal}>
<a href="javascript:void(0)">{number}</a>
<Modal closeTimeoutMS={150}
contentLabel="modalB"
isOpen={isOpen}
onRequestClose={toggleModal}
aria={{
labelledby: "item_title",
describedby: "item_info"
}}>
<h1 id="item_title">Item: {itemNumber}</h1>
<div id="item_info">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur pulvinar varius auctor. Aliquam maximus et justo ut faucibus. Nullam sit amet urna molestie turpis bibendum accumsan a id sem. Proin ullamcorper nisl sapien, gravida dictum nibh congue vel. Vivamus convallis dolor vitae ipsum ultricies, vitae pulvinar justo tincidunt. Maecenas a nunc elit. Phasellus fermentum, tellus ut consectetur scelerisque, eros nunc lacinia eros, aliquet efficitur tellus arcu a nibh. Praesent quis consequat nulla. Etiam dapibus ac sem vel efficitur. Nunc faucibus efficitur leo vitae vulputate. Nunc at quam vitae felis pretium vehicula vel eu quam. Quisque sapien mauris, condimentum eget dictum ut, congue id dolor. Donec vitae varius orci, eu faucibus turpis. Morbi eleifend orci non urna bibendum, ac scelerisque augue efficitur.</p>
</div>
</Modal>
</div>
);
}
}

class List extends Component {
render() {
return this.props.items.map((n, index) => (
<Item key={index} index={index} number={n} />
));
}
}


class NestedModals extends Component {
constructor(props) {
super(props);

this.state = {
isOpen: false,
currentItem: -1,
loading: false,
items: []
};
}

toggleModal = event => {
event.preventDefault();
console.log("NESTEDMODAL", event);
this.setState({
items: [],
isOpen: !this.state.isOpen,
loading: true
});
}

handleOnAfterOpenModal = () => {
// when ready, we can access the available refs.
(new Promise((resolve, reject) => {
setTimeout(() => resolve(true), 500);
})).then(res => {
this.setState({
items: [1, 2, 3, 4, 5].map(x => `Item ${x}`),
loading: false
});
});
}

render() {
const { isOpen } = this.state;
return (
<div>
<button type="button" className="btn btn-primary" onClick={this.toggleModal}>Open Modal A</button>
<Modal
id="test"
closeTimeoutMS={150}
contentLabel="modalA"
isOpen={isOpen}
onAfterOpen={this.handleOnAfterOpenModal}
onRequestClose={this.toggleModal}>
<h1>List of items</h1>
{this.state.loading ? (
<p>Loading...</p>
) : (
<List items={this.state.items} />
)}
</Modal>
</div>
);
}
}

export default {
label: "Working with nested modals.",
app: NestedModals
};
2 changes: 1 addition & 1 deletion examples/basic/react-router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ class App extends Component {
}

export default {
label: "#3. react-modal and react-router.",
label: "react-modal and react-router.",
app: App
};
2 changes: 1 addition & 1 deletion examples/basic/simple_usage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,6 @@ class SimpleUsage extends Component {
}

export default {
label: "#1. Working with one modal at a time.",
label: "Working with one modal at a time.",
app: SimpleUsage
};
30 changes: 30 additions & 0 deletions specs/Modal.events.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-env mocha */
import React from "react";
import "should";
import sinon from "sinon";
import Modal from "react-modal";
import {
moverlay,
mcontent,
Expand Down Expand Up @@ -151,4 +153,32 @@ export default () => {
const event = requestCloseCallback.getCall(0).args[0];
event.should.be.ok();
});

it("on nested modals, only the topmost should handle ESC key.", () => {
const requestCloseCallback = sinon.spy();
const innerRequestCloseCallback = sinon.spy();
let innerModal = null;
let innerModalRef = ref => {
innerModal = ref;
};

renderModal(
{
isOpen: true,
onRequestClose: requestCloseCallback
},
<Modal
isOpen
onRequestClose={innerRequestCloseCallback}
ref={innerModalRef}
>
<span>Test</span>
</Modal>
);

const content = mcontent(innerModal);
escKeyDown(content);
innerRequestCloseCallback.called.should.be.ok();
requestCloseCallback.called.should.not.be.ok();
});
};
2 changes: 1 addition & 1 deletion src/components/ModalPortal.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export default class ModalPortal extends Component {
}

if (this.props.shouldCloseOnEsc && event.keyCode === ESC_KEY) {
event.preventDefault();
event.stopPropagation();
this.requestClose(event);
}
};
Expand Down

0 comments on commit 9a3542a

Please sign in to comment.