Skip to content

Commit b2c347b

Browse files
committedDec 19, 2017
[fixed] stop propagating ESC key event.
This is now required when nesting modals (the event will trigger both handleKeyDown). closes #583.
1 parent 6fc445e commit b2c347b

File tree

8 files changed

+156
-8
lines changed

8 files changed

+156
-8
lines changed
 

‎examples/basic/app.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import SimpleUsage from './simple_usage';
55
import MultipleModals from './multiple_modals';
66
import Forms from './forms';
77
import ReactRouter from './react-router';
8+
import NestedModals from './nested_modals';
89

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

@@ -14,6 +15,7 @@ const examples = [
1415
SimpleUsage,
1516
Forms,
1617
MultipleModals,
18+
NestedModals,
1719
ReactRouter
1820
];
1921

@@ -24,8 +26,8 @@ class App extends Component {
2426
{examples.map((example, key) => {
2527
const ExampleApp = example.app;
2628
return (
27-
<div key={key} className="example">
28-
<h3>{example.label}</h3>
29+
<div key={key + 1} className="example">
30+
<h3>{`#${key + 1}. ${example.label}`}</h3>
2931
<ExampleApp />
3032
</div>
3133
);

‎examples/basic/forms/index.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class Forms extends Component {
3939
<h1 id="heading">Forms!</h1>
4040
<div id="fulldescription" tabIndex="0" role="document">
4141
<p>This is a description of what it does: nothing :)</p>
42-
4342
<form>
4443
<fieldset>
4544
<input type="text" />
@@ -73,6 +72,6 @@ class Forms extends Component {
7372
}
7473

7574
export default {
76-
label: "#3. Modal with forms fields.",
75+
label: "Modal with forms fields.",
7776
app: Forms
7877
};

‎examples/basic/multiple_modals/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,6 @@ class MultipleModals extends Component {
109109
}
110110

111111
export default {
112-
label: "#2. Working with many modal.",
112+
label: "Working with many modal.",
113113
app: MultipleModals
114114
};

‎examples/basic/nested_modals/index.js

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { Component } from 'react';
2+
import Modal from 'react-modal';
3+
4+
class Item extends Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = {
8+
isOpen: false
9+
};
10+
}
11+
12+
toggleModal = index => event => {
13+
console.log("NESTED MODAL ITEM", event);
14+
this.setState({
15+
itemNumber: !this.state.isOpen ? index : null,
16+
isOpen: !this.state.isOpen
17+
});
18+
};
19+
20+
render() {
21+
const { isOpen, itemNumber } = this.state;
22+
const { number, index } = this.props;
23+
24+
const toggleModal = this.toggleModal(index);
25+
26+
return (
27+
<div key={index} onClick={toggleModal}>
28+
<a href="javascript:void(0)">{number}</a>
29+
<Modal closeTimeoutMS={150}
30+
contentLabel="modalB"
31+
isOpen={isOpen}
32+
onRequestClose={toggleModal}
33+
aria={{
34+
labelledby: "item_title",
35+
describedby: "item_info"
36+
}}>
37+
<h1 id="item_title">Item: {itemNumber}</h1>
38+
<div id="item_info">
39+
<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>
40+
</div>
41+
</Modal>
42+
</div>
43+
);
44+
}
45+
}
46+
47+
class List extends Component {
48+
render() {
49+
return this.props.items.map((n, index) => (
50+
<Item key={index} index={index} number={n} />
51+
));
52+
}
53+
}
54+
55+
56+
class NestedModals extends Component {
57+
constructor(props) {
58+
super(props);
59+
60+
this.state = {
61+
isOpen: false,
62+
currentItem: -1,
63+
loading: false,
64+
items: []
65+
};
66+
}
67+
68+
toggleModal = event => {
69+
event.preventDefault();
70+
console.log("NESTEDMODAL", event);
71+
this.setState({
72+
items: [],
73+
isOpen: !this.state.isOpen,
74+
loading: true
75+
});
76+
}
77+
78+
handleOnAfterOpenModal = () => {
79+
// when ready, we can access the available refs.
80+
(new Promise((resolve, reject) => {
81+
setTimeout(() => resolve(true), 500);
82+
})).then(res => {
83+
this.setState({
84+
items: [1, 2, 3, 4, 5].map(x => `Item ${x}`),
85+
loading: false
86+
});
87+
});
88+
}
89+
90+
render() {
91+
const { isOpen } = this.state;
92+
return (
93+
<div>
94+
<button type="button" className="btn btn-primary" onClick={this.toggleModal}>Open Modal A</button>
95+
<Modal
96+
id="test"
97+
closeTimeoutMS={150}
98+
contentLabel="modalA"
99+
isOpen={isOpen}
100+
onAfterOpen={this.handleOnAfterOpenModal}
101+
onRequestClose={this.toggleModal}>
102+
<h1>List of items</h1>
103+
{this.state.loading ? (
104+
<p>Loading...</p>
105+
) : (
106+
<List items={this.state.items} />
107+
)}
108+
</Modal>
109+
</div>
110+
);
111+
}
112+
}
113+
114+
export default {
115+
label: "Working with nested modals.",
116+
app: NestedModals
117+
};

‎examples/basic/react-router/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ class App extends Component {
4242
}
4343

4444
export default {
45-
label: "#3. react-modal and react-router.",
45+
label: "react-modal and react-router.",
4646
app: App
4747
};

‎examples/basic/simple_usage/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,6 @@ class SimpleUsage extends Component {
9090
}
9191

9292
export default {
93-
label: "#1. Working with one modal at a time.",
93+
label: "Working with one modal at a time.",
9494
app: SimpleUsage
9595
};

‎specs/Modal.events.spec.js

+30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-env mocha */
2+
import React from "react";
23
import "should";
34
import sinon from "sinon";
5+
import Modal from "react-modal";
46
import {
57
moverlay,
68
mcontent,
@@ -151,4 +153,32 @@ export default () => {
151153
const event = requestCloseCallback.getCall(0).args[0];
152154
event.should.be.ok();
153155
});
156+
157+
it("on nested modals, only the topmost should handle ESC key.", () => {
158+
const requestCloseCallback = sinon.spy();
159+
const innerRequestCloseCallback = sinon.spy();
160+
let innerModal = null;
161+
let innerModalRef = ref => {
162+
innerModal = ref;
163+
};
164+
165+
renderModal(
166+
{
167+
isOpen: true,
168+
onRequestClose: requestCloseCallback
169+
},
170+
<Modal
171+
isOpen
172+
onRequestClose={innerRequestCloseCallback}
173+
ref={innerModalRef}
174+
>
175+
<span>Test</span>
176+
</Modal>
177+
);
178+
179+
const content = mcontent(innerModal);
180+
escKeyDown(content);
181+
innerRequestCloseCallback.called.should.be.ok();
182+
requestCloseCallback.called.should.not.be.ok();
183+
});
154184
};

‎src/components/ModalPortal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export default class ModalPortal extends Component {
207207
}
208208

209209
if (this.props.shouldCloseOnEsc && event.keyCode === ESC_KEY) {
210-
event.preventDefault();
210+
event.stopPropagation();
211211
this.requestClose(event);
212212
}
213213
};

0 commit comments

Comments
 (0)