Skip to content

Commit 43ff821

Browse files
committed
Fix Popups not firing "close" event when closed programmatically
The native side sends a close event if the popup is closed either by user input, or when disposed from JS, which is all that the "close" method does. However, disposed native objects can not emit events, so this is ignored and the event is missing. Refactor Popup (migrated to TypeScript) to handle close event for all subclasses and fire a close event explicitly if the close method is called (before the internal dispose call). Fix #2151 Change-Id: I403ea2c6e835560446576cd7fd5a0555640a0c8d
1 parent 3c8ca25 commit 43ff821

File tree

12 files changed

+108
-81
lines changed

12 files changed

+108
-81
lines changed

src/tabris/ActionSheet.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export default class ActionSheet extends Popup {
2323
_index: {enumerable: false, writable: true, value: null},
2424
_action: {enumerable: false, writable: true, value: null}
2525
});
26-
this._autoDispose = true;
2726
this._nativeListen('select', true);
2827
}
2928

@@ -36,14 +35,16 @@ export default class ActionSheet extends Popup {
3635
this._index = event.index;
3736
this._action = this.actions[this._index];
3837
super._trigger('select', Object.assign(event, {action: this._action}));
39-
} else if (name === 'close') {
40-
super._trigger('close', Object.assign(event, {index: this._index, action: this._action}));
41-
this.dispose();
4238
} else {
4339
return super._trigger(name, event);
4440
}
4541
}
4642

43+
_handleClose(event) {
44+
Object.assign(event, {index: this._index, action: this._action});
45+
return super._handleClose(event);
46+
}
47+
4748
/** @this {import("../JsxProcessor").default} */
4849
[JSX.jsxFactory](Type, attributes) {
4950
const children = this.getChildren(attributes) || [];

src/tabris/AlertDialog.js

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export default class AlertDialog extends Popup {
2323
constructor(properties) {
2424
super(properties);
2525
this._nativeListen('close', true);
26-
this._autoDispose = true;
2726
}
2827

2928
get _nativeType() {
@@ -56,21 +55,16 @@ export default class AlertDialog extends Popup {
5655
return super.open();
5756
}
5857

59-
_trigger(name, event) {
60-
if (name === 'close') {
61-
event.button = event.button || null;
62-
event.texts = [];
63-
if (this._contentView) {
64-
event.texts = this._contentView.children().toArray().map(textInput => textInput.text);
65-
}
66-
if (event.button) {
67-
super._trigger('close' + capitalizeFirstChar(event.button), event);
68-
}
69-
super._trigger('close', event);
70-
this.dispose();
71-
} else {
72-
return super._trigger(name, event);
58+
_handleClose(event) {
59+
event.button = event.button || null;
60+
event.texts = [];
61+
if (this._contentView) {
62+
event.texts = this._contentView.children().toArray().map(textInput => textInput.text);
63+
}
64+
if (event.button) {
65+
this._trigger('close' + capitalizeFirstChar(event.button), event);
7366
}
67+
return super._handleClose(event);
7468
}
7569

7670
_dispose() {

src/tabris/DateDialog.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,22 @@ export default class DateDialog extends Popup {
2323
super(properties);
2424
this._nativeListen('close', true);
2525
this._nativeListen('select', true);
26-
this._autoDispose = true;
2726
}
2827

2928
get _nativeType() {
3029
return 'tabris.DateDialog';
3130
}
3231

3332
_trigger(name, event) {
34-
if (name === 'close') {
35-
this._handleCloseEvent(event);
36-
} else if (name === 'select') {
33+
if (name === 'select') {
3734
event.date = new Date(event.date);
3835
super._trigger('select', event);
39-
this._handleCloseEvent(event);
36+
return this._handleClose(event);
4037
} else {
4138
return super._trigger(name, event);
4239
}
4340
}
4441

45-
_handleCloseEvent(event) {
46-
super._trigger('close', event);
47-
this.dispose();
48-
}
49-
5042
}
5143

5244
NativeObject.defineProperties(DateDialog.prototype, {

src/tabris/Popover.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,13 @@ export default class Popover extends Popup {
2323
super(properties);
2424
Object.defineProperty(this, 'contentView', {value: createContentView()});
2525
this._nativeListen('close', true);
26-
this._autoDispose = true;
2726
this._nativeSet('contentView', this.contentView.cid);
2827
}
2928

3029
get _nativeType() {
3130
return 'tabris.Popover';
3231
}
3332

34-
_trigger(name, event) {
35-
if (name === 'close') {
36-
super._trigger('close', event);
37-
this.dispose();
38-
} else {
39-
return super._trigger(name, event);
40-
}
41-
}
42-
4333
_dispose() {
4434
if (!this.isDisposed()) {
4535
Composite.prototype._dispose.call(this.contentView);

src/tabris/Popup.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/tabris/Popup.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import NativeObject from './NativeObject';
2+
import JsxProcessor, {JSX} from './JsxProcessor';
3+
4+
export default class Popup extends NativeObject {
5+
6+
open() {
7+
if (this.isDisposed()) {
8+
throw new Error('Can not open a popup that was disposed');
9+
}
10+
this._nativeCall('open', {});
11+
return this;
12+
}
13+
14+
close() {
15+
this._handleClose({});
16+
return this;
17+
}
18+
19+
protected _trigger(name: string, event: {}): boolean {
20+
if (name === 'close') {
21+
return this._handleClose(event);
22+
} else {
23+
return super._trigger(name, event);
24+
}
25+
}
26+
27+
protected _handleClose(eventData: object) {
28+
if (this._isDisposed || this._inDispose) {
29+
return false;
30+
}
31+
super._trigger('close', eventData);
32+
this.dispose();
33+
return false;
34+
}
35+
36+
protected [JSX.jsxFactory](this: JsxProcessor, Type: Constructor<Popup>, attributes: object) {
37+
return this.createNativeObject(Type, attributes);
38+
}
39+
40+
}

src/tabris/TimeDialog.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,22 @@ export default class TimeDialog extends Popup {
2323
super(properties);
2424
this._nativeListen('close', true);
2525
this._nativeListen('select', true);
26-
this._autoDispose = true;
2726
}
2827

2928
get _nativeType() {
3029
return 'tabris.TimeDialog';
3130
}
3231

3332
_trigger(name, event) {
34-
if (name === 'close') {
35-
this._handleCloseEvent(event);
36-
} else if (name === 'select') {
33+
if (name === 'select') {
3734
event.date = new Date(event.date);
3835
super._trigger('select', event);
39-
this._handleCloseEvent(event);
36+
return this._handleClose(event);
4037
} else {
4138
return super._trigger(name, event);
4239
}
4340
}
4441

45-
_handleCloseEvent(event) {
46-
super._trigger('close', event);
47-
this.dispose();
48-
}
49-
5042
}
5143

5244
NativeObject.defineProperties(TimeDialog.prototype, {

test/tabris/ActionSheet.test.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ describe('ActionSheet', () => {
134134
expect(actionSheet.isDisposed()).to.equal(true);
135135
});
136136

137+
it('fires close event with no selection', () => {
138+
const close = spy();
139+
actionSheet.onClose(close);
140+
141+
actionSheet.close();
142+
143+
expect(close).to.have.been.calledOnce;
144+
expect(close).to.have.been.calledWithMatch({target: actionSheet, index: null, action: null});
145+
});
146+
137147
});
138148

139149
describe('close event', () => {
@@ -150,14 +160,11 @@ describe('ActionSheet', () => {
150160
});
151161

152162
it('fires close event with no selection', () => {
153-
const closeOk = spy();
154163
const close = spy();
155-
actionSheet.on('closeOk', closeOk);
156164
actionSheet.onClose(close);
157165

158166
tabris._notify(actionSheet.cid, 'close', {});
159167

160-
expect(closeOk).not.to.have.been.called;
161168
expect(close).to.have.been.calledOnce;
162169
expect(close).to.have.been.calledWithMatch({target: actionSheet, index: null, action: null});
163170
});

test/tabris/AlertDialog.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ describe('AlertDialog', function() {
223223
expect(dialog.isDisposed()).to.equal(true);
224224
});
225225

226+
it('fires close event', function() {
227+
const close = spy();
228+
dialog.onClose(close);
229+
230+
dialog.close();
231+
232+
expect(close).to.have.been.calledOnce;
233+
expect(close).to.have.been.calledWithMatch({target: dialog, button: null, texts: []});
234+
});
235+
226236
});
227237

228238
describe('close event', function() {

test/tabris/DateDialog.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ describe('DateDialog', function() {
144144
expect(dialog.isDisposed()).to.equal(true);
145145
});
146146

147+
it('fires close event', function() {
148+
const close = spy();
149+
dialog.onClose(close);
150+
151+
dialog.close();
152+
153+
expect(close).to.have.been.calledOnce;
154+
expect(close).to.have.been.calledWithMatch({target: dialog});
155+
});
156+
147157
});
148158

149159
describe('close event', function() {

0 commit comments

Comments
 (0)