diff --git a/src/app/shared/components/modal-host/modal-host.component.spec.ts b/src/app/shared/components/modal-host/modal-host.component.spec.ts index fd5af67..4946a52 100644 --- a/src/app/shared/components/modal-host/modal-host.component.spec.ts +++ b/src/app/shared/components/modal-host/modal-host.component.spec.ts @@ -3,25 +3,10 @@ import { Component, inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { TestBed, getTestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { - BrowserTestingModule, - platformBrowserTesting -} from '@angular/platform-browser/testing'; -import { - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, - vi -} from 'vitest'; +import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -import { - MODAL_DATA, - ModalRef, - ModalService -} from '../../../core/services/modal.service'; +import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service'; import { ModalHostComponent } from './modal-host.component'; @Component({ @@ -31,7 +16,7 @@ import { ModalHostComponent } from './modal-host.component'; {{ data?.title }} - ` + `, }) class ModalContentTestComponent { readonly data = inject<{ title: string } | null>(MODAL_DATA); @@ -48,10 +33,7 @@ describe('ModalHostComponent', () => { beforeAll(() => { try { - getTestBed().initTestEnvironment( - BrowserTestingModule, - platformBrowserTesting() - ); + getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting()); } catch { // Test environment may already be initialized by another setup entrypoint. } @@ -59,7 +41,7 @@ describe('ModalHostComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ModalHostComponent] + imports: [ModalHostComponent], }).compileComponents(); service = TestBed.inject(ModalService); @@ -68,6 +50,7 @@ describe('ModalHostComponent', () => { afterEach(() => { doc.body.style.overflow = ''; + doc.body.style.paddingRight = ''; TestBed.resetTestingModule(); }); @@ -84,7 +67,7 @@ describe('ModalHostComponent', () => { service.open(ModalContentTestComponent, { title: 'Editar producto', - data: { title: 'Contenido del modal' } + data: { title: 'Contenido del modal' }, }); fixture.detectChanges(); @@ -99,27 +82,30 @@ describe('ModalHostComponent', () => { it('closes with a result from the child component', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { - data: { title: 'Cerrar' } + data: { title: 'Cerrar' }, }); const closedSpy = vi.fn(); ref.afterClosed$.subscribe(closedSpy); fixture.detectChanges(); - const closeButton = fixture.nativeElement.querySelector('.modal-test-close') as HTMLButtonElement; + const closeButton = fixture.nativeElement.querySelector( + '.modal-test-close', + ) as HTMLButtonElement; closeButton.click(); fixture.detectChanges(); expect(closedSpy).toHaveBeenCalledWith('accepted'); expect(service.activeModal()).toBeNull(); expect(doc.body.style.overflow).toBe(''); + expect(doc.body.style.paddingRight).toBe(''); }); it('closes on backdrop click when enabled', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { data: { title: 'Backdrop' }, - closeOnBackdrop: true + closeOnBackdrop: true, }); fixture.detectChanges(); @@ -136,7 +122,7 @@ describe('ModalHostComponent', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { data: { title: 'Persistente' }, - closeOnBackdrop: false + closeOnBackdrop: false, }); fixture.detectChanges(); @@ -152,7 +138,7 @@ describe('ModalHostComponent', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { data: { title: 'Escape' }, - closeOnEscape: true + closeOnEscape: true, }); fixture.detectChanges(); @@ -167,7 +153,7 @@ describe('ModalHostComponent', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { data: { title: 'No Escape' }, - closeOnEscape: false + closeOnEscape: false, }); fixture.detectChanges(); @@ -181,12 +167,13 @@ describe('ModalHostComponent', () => { const fixture = TestBed.createComponent(ModalHostComponent); const ref = service.open(ModalContentTestComponent, { title: 'Con cierre', - data: { title: 'Boton' } + data: { title: 'Boton' }, }); fixture.detectChanges(); - const closeButton = fixture.debugElement.query(By.css('.btn-close')).nativeElement as HTMLButtonElement; + const closeButton = fixture.debugElement.query(By.css('.btn-close')) + .nativeElement as HTMLButtonElement; closeButton.click(); fixture.detectChanges(); diff --git a/src/app/shared/components/modal-host/modal-host.component.ts b/src/app/shared/components/modal-host/modal-host.component.ts index 7bc840e..a85e7e9 100644 --- a/src/app/shared/components/modal-host/modal-host.component.ts +++ b/src/app/shared/components/modal-host/modal-host.component.ts @@ -7,21 +7,17 @@ import { computed, effect, inject, - viewChild + viewChild, } from '@angular/core'; -import { - MODAL_DATA, - ModalRef, - ModalService -} from '../../../core/services/modal.service'; +import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service'; import { ModalShellComponent } from '../modal-shell/modal-shell.component'; @Component({ selector: 'app-modal-host', imports: [NgComponentOutlet, ModalShellComponent], templateUrl: './modal-host.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ModalHostComponent { private readonly modalService = inject(ModalService); @@ -42,9 +38,9 @@ export class ModalHostComponent { return Injector.create({ providers: [ { provide: ModalRef, useValue: modal.ref }, - { provide: MODAL_DATA, useValue: modal.config.data ?? null } + { provide: MODAL_DATA, useValue: modal.config.data ?? null }, ], - parent: this.injector + parent: this.injector, }); }); @@ -58,6 +54,18 @@ export class ModalHostComponent { const body = this.document.body; const previousOverflow = body.style.overflow; + const previousPaddingRight = body.style.paddingRight; + const view = this.document.defaultView; + const scrollbarWidth = view + ? Math.max(0, view.innerWidth - this.document.documentElement.clientWidth) + : 0; + + if (scrollbarWidth > 0 && view) { + const currentPaddingRight = + Number.parseFloat(view.getComputedStyle(body).paddingRight) || 0; + body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`; + } + body.style.overflow = 'hidden'; const onKeyDown = (event: KeyboardEvent) => { @@ -75,6 +83,7 @@ export class ModalHostComponent { onCleanup(() => { this.document.removeEventListener('keydown', onKeyDown); body.style.overflow = previousOverflow; + body.style.paddingRight = previousPaddingRight; }); }); } diff --git a/src/app/shared/components/modal-shell/modal-shell.component.scss b/src/app/shared/components/modal-shell/modal-shell.component.scss index d5f80b8..d016d61 100644 --- a/src/app/shared/components/modal-shell/modal-shell.component.scss +++ b/src/app/shared/components/modal-shell/modal-shell.component.scss @@ -10,8 +10,7 @@ align-items: center; justify-content: center; padding: 1rem; - background: rgba(16, 18, 22, 0.48); - backdrop-filter: blur(2px); + background: rgba(16, 18, 22, 0.56); } .modal-shell__dialog {