diff --git a/src/app/social/social.service.ts b/src/app/social/social.service.ts index 2c6527cee1b..4f37d32f364 100644 --- a/src/app/social/social.service.ts +++ b/src/app/social/social.service.ts @@ -78,6 +78,9 @@ export class SocialService { * Import the AddToAny JavaScript */ initializeAddToAnyScript(): any { + if (!this.enabled) { + return; + } // Initializing the addToAny script const script = this._document.createElement('script'); script.type = 'text/javascript'; @@ -96,10 +99,63 @@ export class SocialService { } else { this._document.head.appendChild(script); clearInterval(bodyHeightInterval); + this.observeAddToAnyModal(); } }, 200); } + /** + * Observes DOM changes to detect when the AddToAny modal is opened or closed. + * Since AddToAny does not reliably expose lifecycle callbacks, a MutationObserver + * is used to monitor the presence and visibility of the modal element. + * + * When the modal is visible, background scrolling is disabled by setting + * `overflow: hidden` on the document body. When the modal is no longer visible, + * the original scrolling behavior is restored. + */ + private observeAddToAnyModal(): void { + if (!isPlatformBrowser(this.platformId)) { + return; + } + + const body = this._document.body; + let isLocked = false; + + const observer = new MutationObserver(() => { + const modal = this._document.querySelector('.a2a_full') as HTMLElement; + const isVisible = modal && this.isElementVisible(modal); + + if (isVisible && !isLocked) { + body.style.overflow = 'hidden'; + isLocked = true; + } else if (!isVisible && isLocked) { + body.style.overflow = ''; + isLocked = false; + } + }); + + observer.observe(body, { + childList: true, + subtree: true, + attributes: true, + }); + } + + /** + * Determines whether a given HTML element is currently visible in the DOM. + * This is necessary because the AddToAny modal may remain in the DOM while hidden. + * + * @param element The element to check for visibility + * @returns true if the element is visible, false otherwise + */ + private isElementVisible(element: HTMLElement): boolean { + return !!( + element.offsetWidth || + element.offsetHeight || + element.getClientRects().length + ); + } + /** * Initialize the Social service. This method must be called only inside app component. */ diff --git a/src/app/submission/submission.service.spec.ts b/src/app/submission/submission.service.spec.ts index a7d77dd0389..d905a97ebbc 100644 --- a/src/app/submission/submission.service.spec.ts +++ b/src/app/submission/submission.service.spec.ts @@ -979,7 +979,17 @@ describe('SubmissionService test suite', () => { describe('redirectToEditItem', () => { beforeEach(() => { - (itemService.findById as jasmine.Spy).calls.reset(); + if ((itemService.findById as any).calls) { + (itemService.findById as jasmine.Spy).calls.reset(); + } else { + spyOn(itemService as any, 'findById'); + } + + if ((requestServce.setStaleByHrefSubstring as any).calls) { + (requestServce.setStaleByHrefSubstring as jasmine.Spy).calls.reset(); + } else { + spyOn(requestServce as any, 'setStaleByHrefSubstring'); + } }); it('should redirect to Item page', fakeAsync(() => { @@ -1003,8 +1013,8 @@ describe('SubmissionService test suite', () => { }), }); let itemSubmissionId = itemUuid + ':FULL'; - spyOn(itemService as any, 'findById').and.returnValue(cold('a', { a: createSuccessfulRemoteDataObject(mockItem) })); - spyOn(requestServce as any, 'setStaleByHrefSubstring').and.returnValue(cold('a', { a: true })); + (itemService.findById as jasmine.Spy).and.returnValue(cold('a', { a: createSuccessfulRemoteDataObject(mockItem) })); + (requestServce.setStaleByHrefSubstring as jasmine.Spy).and.returnValue(cold('a', { a: true })); scheduler.schedule(() => service.invalidateCacheAndRedirectToItemPage(itemSubmissionId)); scheduler.flush();