Skip to content

Commit 089ae9b

Browse files
committed
fix(core) Remove floating if reference is not connected
1 parent 49e8136 commit 089ae9b

File tree

6 files changed

+42
-23
lines changed

6 files changed

+42
-23
lines changed

apps/ngx-popovers/src/app/pages/documentation/ui/components/code-example-tabs/code-example-tabs.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const tabTypeToLabel: Record<TabType, string> = {
2121
[TabType.EXAMPLE]: 'Example',
2222
[TabType.HTML]: 'HTML',
2323
[TabType.TS]: 'Typescript',
24-
[TabType.SCSS]: 'scss'
24+
[TabType.SCSS]: 'SCSS'
2525
};
2626

2727
@Component({

apps/ngx-popovers/src/app/shared/tabs/tab/tab.component.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,8 @@ export class TabComponent<T = any> {
3838
this.index.set(index);
3939
}
4040

41-
constructor() {
42-
effect(() => {
43-
console.log('value!', this.value());
44-
});
45-
}
46-
4741
@HostListener('click')
4842
onClick() {
49-
5043
this.tabs.selectTab(this.value() || this.index());
5144
}
5245
}

apps/ngx-popovers/src/app/shared/tabs/tabs.component.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export class TabsComponent<T> {
3030
}
3131

3232
selectTab(index: T) {
33-
console.log('selected tab', index);
3433
this.active.set(index);
3534
}
3635
}

packages/core/src/lib/floating/floating.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@if (!isServer) {
1+
@if (!isServer && referenceConnected()) {
22
<ngx-portal [bindTo]="bindTo()">
33
<div
44
#floating

packages/core/src/lib/floating/floating.component.spec.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ describe('FloatingComponent', () => {
2121
fixture.detectChanges();
2222
});
2323

24-
const floating = () => document.querySelector('.floating') as HTMLElement;
24+
const floating = (): HTMLElement => document.querySelector('.floating')!;
2525

2626
const parentId = 'parent-id';
2727

28-
const createParent = () => {
28+
const createReference = () => {
2929
const parent = document.createElement('div');
3030
parent.id = parentId;
3131
document.body.appendChild(parent);
@@ -44,21 +44,21 @@ describe('FloatingComponent', () => {
4444
});
4545

4646
it('should call bind when placement changes', () => {
47-
const bind = jest.spyOn(component, 'bind');
47+
const bind = jest.spyOn(component, 'bindToReference');
4848

4949
fixture.componentRef.setInput('placement', 'top');
5050
fixture.detectChanges();
5151
expect(bind).toHaveBeenCalled();
5252
});
5353

5454
it('should call bind when any prop changes', () => {
55-
const bind = jest.spyOn(component, 'bind');
55+
const bind = jest.spyOn(component, 'bindToReference');
5656
component.ngOnChanges();
5757
expect(bind).toHaveBeenCalled();
5858
});
5959

6060
it('should call bind after content init', () => {
61-
const bind = jest.spyOn(component, 'bind');
61+
const bind = jest.spyOn(component, 'bindToReference');
6262
component.ngAfterViewInit();
6363
expect(bind).toHaveBeenCalled();
6464
});
@@ -94,7 +94,7 @@ describe('FloatingComponent', () => {
9494
});
9595

9696
it('should emit when click outside', () => {
97-
const trigger = createParent();
97+
const trigger = createReference();
9898
fixture.componentRef.setInput('trigger', trigger);
9999
const outside = jest.spyOn(component.clickedOutside, 'emit');
100100
document.body.click();
@@ -117,10 +117,20 @@ describe('FloatingComponent', () => {
117117
}
118118
});
119119

120-
await component.bind();
120+
await component.bindToReference();
121121
await awaitTime();
122122
expect(compute).toHaveBeenCalled();
123123
});
124+
125+
it('should be removed if reference is not exist', () => {
126+
const div = createReference();
127+
fixture.componentRef.setInput('reference', div);
128+
fixture.detectChanges();
129+
130+
div.remove();
131+
fixture.detectChanges();
132+
expect(floating()).toBeFalsy();
133+
});
124134
});
125135

126136
describe('FloatingComponent.DI', () => {

packages/core/src/lib/floating/floating.component.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import {
33
booleanAttribute,
44
Component,
55
computed,
6+
DoCheck,
7+
effect,
68
ElementRef,
79
inject,
810
input,
911
model,
1012
OnChanges,
1113
OnDestroy,
1214
output,
15+
signal,
1316
viewChild
1417
} from '@angular/core';
1518
import { CommonModule } from '@angular/common';
@@ -40,7 +43,7 @@ import { isServer } from '../injections';
4043
templateUrl: './floating.component.html',
4144
styleUrl: './floating.component.scss'
4245
})
43-
export class FloatingComponent implements AfterViewInit, OnChanges, OnDestroy {
46+
export class FloatingComponent implements AfterViewInit, OnChanges, DoCheck, OnDestroy {
4447
readonly config = inject(NGX_FLOATING_CONFIG);
4548
readonly floatingService = inject(FloatingService);
4649
readonly isServer = isServer();
@@ -53,7 +56,6 @@ export class FloatingComponent implements AfterViewInit, OnChanges, OnDestroy {
5356
*/
5457
trigger = input<HTMLElement>();
5558
reference = input<ReferenceElement>();
56-
5759
/**
5860
* TODO: remove and use reference after v.18
5961
*/
@@ -70,6 +72,9 @@ export class FloatingComponent implements AfterViewInit, OnChanges, OnDestroy {
7072
clickedInside = output<Element>();
7173
computePositionReturn = output<ComputePositionReturn>();
7274

75+
/* Is reference element exist in the DOM */
76+
referenceConnected = signal(false);
77+
7378
readonly arrowMiddleware = computed(() => this.getArrowMiddleware(this.arrow()));
7479

7580
private _computePosition$ = new BehaviorSubject<ComputePositionReturn | undefined>(undefined);
@@ -86,18 +91,30 @@ export class FloatingComponent implements AfterViewInit, OnChanges, OnDestroy {
8691
cleanup?: () => void;
8792

8893
ngAfterViewInit() {
89-
return this.bind();
94+
return this.bindToReference();
9095
}
9196

9297
ngOnChanges() {
93-
return this.bind();
98+
return this.bindToReference();
99+
}
100+
101+
ngDoCheck() {
102+
this.referenceConnected.set((this._reference() as HTMLElement)?.isConnected);
94103
}
95104

96105
ngOnDestroy() {
97106
this.cleanup?.();
98107
}
99108

100-
async bind() {
109+
constructor() {
110+
effect(() => {
111+
if (this.referenceConnected()) {
112+
this.bindToReference();
113+
}
114+
});
115+
}
116+
117+
async bindToReference() {
101118
if (this.isServer) {
102119
return;
103120
}
@@ -156,7 +173,7 @@ export class FloatingComponent implements AfterViewInit, OnChanges, OnDestroy {
156173
*/
157174
setArrow(arrow: Arrow) {
158175
this.arrow.set(arrow);
159-
return this.bind();
176+
return this.bindToReference();
160177
}
161178

162179
private getArrowMiddleware(arr?: Arrow) {

0 commit comments

Comments
 (0)