Skip to content

Commit 2cdd4c0

Browse files
committed
fix: replace event service with fromEvent and unsubscribe on dialog destroy
Signed-off-by: Akshat Patel <[email protected]>
1 parent d4000a8 commit 2cdd4c0

File tree

2 files changed

+64
-42
lines changed

2 files changed

+64
-42
lines changed

src/dialog/dialog.directive.ts

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { DialogService } from "./dialog.service";
1717
import { CloseMeta, CloseReasons, DialogConfig } from "./dialog-config.interface";
1818
import { EventService } from "carbon-components-angular/utils";
1919
import { Dialog } from "./dialog.component";
20+
import { fromEvent, Subscription } from "rxjs";
2021

2122
/**
2223
* A generic directive that can be inherited from to create dialogs (for example, a tooltip or popover)
@@ -125,6 +126,8 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
125126
*/
126127
protected dialogRef: ComponentRef<Dialog>;
127128

129+
private subscriptions: Subscription[] = [];
130+
128131
/**
129132
* Creates an instance of DialogDirective.
130133
* @param elementRef
@@ -136,8 +139,11 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
136139
protected elementRef: ElementRef,
137140
protected viewContainerRef: ViewContainerRef,
138141
protected dialogService: DialogService,
142+
/**
143+
* Deprecated as of v5
144+
*/
139145
protected eventService: EventService
140-
) {}
146+
) { }
141147

142148
ngOnChanges(changes: SimpleChanges) {
143149
// set the config object (this can [and should!] be added to in child classes depending on what they need)
@@ -172,6 +178,7 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
172178
this.updateConfig();
173179
}
174180

181+
175182
/**
176183
* Sets the config object and binds events for hovering or clicking before
177184
* running code from child class.
@@ -180,49 +187,55 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
180187
// fix for safari hijacking clicks
181188
this.dialogService.singletonClickListen();
182189

183-
const element = this.elementRef.nativeElement;
190+
const element: HTMLElement = this.elementRef.nativeElement;
184191

185-
this.eventService.on(element, "keydown", (event: KeyboardEvent) => {
186-
if (event.target === this.dialogConfig.parentRef.nativeElement &&
187-
(event.key === "Tab" || event.key === "Tab" && event.shiftKey) ||
188-
event.key === "Escape") {
189-
this.close({
190-
reason: CloseReasons.interaction,
191-
target: event.target
192-
});
193-
}
194-
});
192+
this.subscriptions.push(
193+
fromEvent(element, "keydown").subscribe((event: KeyboardEvent) => {
194+
if (event.target === this.dialogConfig.parentRef.nativeElement &&
195+
(event.key === "Tab" || event.key === "Tab" && event.shiftKey) ||
196+
event.key === "Escape") {
197+
this.close({
198+
reason: CloseReasons.interaction,
199+
target: event.target
200+
});
201+
}
202+
})
203+
);
195204

196205
// bind events for hovering or clicking the host
197206
if (this.trigger === "hover" || this.trigger === "mouseenter") {
198-
this.eventService.on(element, "mouseenter", this.open.bind(this));
199-
this.eventService.on(element, this.closeTrigger, (event) => {
200-
this.close({
201-
reason: CloseReasons.interaction,
202-
target: event.target
203-
});
204-
});
205-
this.eventService.on(element, "focus", this.open.bind(this));
206-
this.eventService.on(element, "blur", (event) => {
207-
this.close({
208-
reason: CloseReasons.interaction,
209-
target: event.target
210-
});
211-
});
207+
this.subscriptions.push(
208+
fromEvent(element, "mouseenter").subscribe(() => this.open()),
209+
fromEvent(element, this.closeTrigger).subscribe((event) => {
210+
this.close({
211+
reason: CloseReasons.interaction,
212+
target: event.target
213+
});
214+
}),
215+
fromEvent(element, "focus").subscribe(() => this.open()),
216+
fromEvent(element, "blur").subscribe((event) => {
217+
this.close({
218+
reason: CloseReasons.interaction,
219+
target: event.target
220+
});
221+
})
222+
);
212223
} else {
213-
this.eventService.on(element, "click", (event) => {
214-
this.toggle({
215-
reason: CloseReasons.interaction,
216-
target: event.target
217-
});
218-
});
219-
this.eventService.on(element, "keydown", (event: KeyboardEvent) => {
220-
if (event.key === "Enter" || event.key === " ") {
221-
setTimeout(() => {
222-
this.open();
224+
this.subscriptions.push(
225+
fromEvent(element, "click").subscribe((event) => {
226+
this.toggle({
227+
reason: CloseReasons.interaction,
228+
target: event.target
223229
});
224-
}
225-
});
230+
}),
231+
fromEvent(element, "keydown").subscribe((event: KeyboardEvent) => {
232+
if (event.key === "Enter" || event.key === " ") {
233+
setTimeout(() => {
234+
this.open();
235+
});
236+
}
237+
})
238+
);
226239
}
227240

228241
DialogDirective.dialogCounter++;
@@ -241,6 +254,7 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
241254
this.close({
242255
reason: CloseReasons.destroyed
243256
});
257+
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
244258
}
245259

246260
/**
@@ -259,7 +273,7 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
259273

260274
// Handles emitting all the close events to clean everything up
261275
// Also enforce accessibility on close by updating an aria attr on the nativeElement.
262-
this.dialogRef.instance.close.subscribe((meta: CloseMeta) => {
276+
const subscription = this.dialogRef.instance.close.subscribe((meta: CloseMeta) => {
263277
if (!this.dialogRef) { return; }
264278
if (this.dialogConfig.shouldClose && this.dialogConfig.shouldClose(meta)) {
265279
// close the dialog, emit events, and clear out the open states
@@ -269,6 +283,7 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
269283
this.onClose.emit();
270284
this.isOpenChange.emit(false);
271285
}
286+
subscription.unsubscribe();
272287
});
273288

274289
return this.dialogRef;
@@ -298,13 +313,13 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
298313
* Empty method for child classes to override and specify additional init steps.
299314
* Run after DialogDirective completes it's ngOnInit.
300315
*/
301-
protected onDialogInit() {}
316+
protected onDialogInit() { }
302317

303318
/**
304319
* Empty method for child to override and specify additional on changes steps.
305320
* run after DialogDirective completes it's ngOnChanges.
306321
*/
307-
protected onDialogChanges(_changes: SimpleChanges) {}
322+
protected onDialogChanges(_changes: SimpleChanges) { }
308323

309-
protected updateConfig() {}
324+
protected updateConfig() { }
310325
}

src/dialog/overflow-menu/overflow-menu.stories.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ export default {
3333
const Template = (args) => ({
3434
props: args,
3535
template: `
36+
<button (click)="show=!show">Click me!</button>
37+
<br/><br/><br/><br/><br/><br/>
38+
3639
<cds-overflow-menu
40+
*ngIf="show"
3741
[placement]="placement"
3842
[open]="open"
3943
[flip]="flip"
@@ -50,10 +54,13 @@ const Template = (args) => ({
5054
<cds-overflow-menu-option type="danger" (selected)="selected($event)">Danger option</cds-overflow-menu-option>
5155
</cds-overflow-menu>
5256
<cds-placeholder></cds-placeholder>
57+
58+
5359
`
5460
});
5561
export const Basic = Template.bind({});
5662
Basic.args = {
63+
show: true,
5764
open: false,
5865
flip: false,
5966
offset: {

0 commit comments

Comments
 (0)