feat: improve modal styling and enhance padding management in modal host component
This commit is contained in:
@@ -3,25 +3,10 @@ import { Component, inject } from '@angular/core';
|
|||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { TestBed, getTestBed } from '@angular/core/testing';
|
import { TestBed, getTestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import {
|
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
|
||||||
BrowserTestingModule,
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
platformBrowserTesting
|
|
||||||
} from '@angular/platform-browser/testing';
|
|
||||||
import {
|
|
||||||
afterEach,
|
|
||||||
beforeAll,
|
|
||||||
beforeEach,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
it,
|
|
||||||
vi
|
|
||||||
} from 'vitest';
|
|
||||||
|
|
||||||
import {
|
import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service';
|
||||||
MODAL_DATA,
|
|
||||||
ModalRef,
|
|
||||||
ModalService
|
|
||||||
} from '../../../core/services/modal.service';
|
|
||||||
import { ModalHostComponent } from './modal-host.component';
|
import { ModalHostComponent } from './modal-host.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -31,7 +16,7 @@ import { ModalHostComponent } from './modal-host.component';
|
|||||||
<span class="modal-test-title">{{ data?.title }}</span>
|
<span class="modal-test-title">{{ data?.title }}</span>
|
||||||
<button type="button" class="modal-test-close" (click)="close()">Cerrar</button>
|
<button type="button" class="modal-test-close" (click)="close()">Cerrar</button>
|
||||||
</div>
|
</div>
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
class ModalContentTestComponent {
|
class ModalContentTestComponent {
|
||||||
readonly data = inject<{ title: string } | null>(MODAL_DATA);
|
readonly data = inject<{ title: string } | null>(MODAL_DATA);
|
||||||
@@ -48,10 +33,7 @@ describe('ModalHostComponent', () => {
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
try {
|
try {
|
||||||
getTestBed().initTestEnvironment(
|
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
|
||||||
BrowserTestingModule,
|
|
||||||
platformBrowserTesting()
|
|
||||||
);
|
|
||||||
} catch {
|
} catch {
|
||||||
// Test environment may already be initialized by another setup entrypoint.
|
// Test environment may already be initialized by another setup entrypoint.
|
||||||
}
|
}
|
||||||
@@ -59,7 +41,7 @@ describe('ModalHostComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ModalHostComponent]
|
imports: [ModalHostComponent],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
service = TestBed.inject(ModalService);
|
service = TestBed.inject(ModalService);
|
||||||
@@ -68,6 +50,7 @@ describe('ModalHostComponent', () => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
doc.body.style.overflow = '';
|
doc.body.style.overflow = '';
|
||||||
|
doc.body.style.paddingRight = '';
|
||||||
TestBed.resetTestingModule();
|
TestBed.resetTestingModule();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,7 +67,7 @@ describe('ModalHostComponent', () => {
|
|||||||
|
|
||||||
service.open(ModalContentTestComponent, {
|
service.open(ModalContentTestComponent, {
|
||||||
title: 'Editar producto',
|
title: 'Editar producto',
|
||||||
data: { title: 'Contenido del modal' }
|
data: { title: 'Contenido del modal' },
|
||||||
});
|
});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@@ -99,27 +82,30 @@ describe('ModalHostComponent', () => {
|
|||||||
it('closes with a result from the child component', () => {
|
it('closes with a result from the child component', () => {
|
||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
data: { title: 'Cerrar' }
|
data: { title: 'Cerrar' },
|
||||||
});
|
});
|
||||||
const closedSpy = vi.fn();
|
const closedSpy = vi.fn();
|
||||||
|
|
||||||
ref.afterClosed$.subscribe(closedSpy);
|
ref.afterClosed$.subscribe(closedSpy);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const closeButton = fixture.nativeElement.querySelector('.modal-test-close') as HTMLButtonElement;
|
const closeButton = fixture.nativeElement.querySelector(
|
||||||
|
'.modal-test-close',
|
||||||
|
) as HTMLButtonElement;
|
||||||
closeButton.click();
|
closeButton.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(closedSpy).toHaveBeenCalledWith('accepted');
|
expect(closedSpy).toHaveBeenCalledWith('accepted');
|
||||||
expect(service.activeModal()).toBeNull();
|
expect(service.activeModal()).toBeNull();
|
||||||
expect(doc.body.style.overflow).toBe('');
|
expect(doc.body.style.overflow).toBe('');
|
||||||
|
expect(doc.body.style.paddingRight).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes on backdrop click when enabled', () => {
|
it('closes on backdrop click when enabled', () => {
|
||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
data: { title: 'Backdrop' },
|
data: { title: 'Backdrop' },
|
||||||
closeOnBackdrop: true
|
closeOnBackdrop: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -136,7 +122,7 @@ describe('ModalHostComponent', () => {
|
|||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
data: { title: 'Persistente' },
|
data: { title: 'Persistente' },
|
||||||
closeOnBackdrop: false
|
closeOnBackdrop: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -152,7 +138,7 @@ describe('ModalHostComponent', () => {
|
|||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
data: { title: 'Escape' },
|
data: { title: 'Escape' },
|
||||||
closeOnEscape: true
|
closeOnEscape: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -167,7 +153,7 @@ describe('ModalHostComponent', () => {
|
|||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
data: { title: 'No Escape' },
|
data: { title: 'No Escape' },
|
||||||
closeOnEscape: false
|
closeOnEscape: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -181,12 +167,13 @@ describe('ModalHostComponent', () => {
|
|||||||
const fixture = TestBed.createComponent(ModalHostComponent);
|
const fixture = TestBed.createComponent(ModalHostComponent);
|
||||||
const ref = service.open(ModalContentTestComponent, {
|
const ref = service.open(ModalContentTestComponent, {
|
||||||
title: 'Con cierre',
|
title: 'Con cierre',
|
||||||
data: { title: 'Boton' }
|
data: { title: 'Boton' },
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
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();
|
closeButton.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
|||||||
@@ -7,21 +7,17 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
effect,
|
effect,
|
||||||
inject,
|
inject,
|
||||||
viewChild
|
viewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import {
|
import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service';
|
||||||
MODAL_DATA,
|
|
||||||
ModalRef,
|
|
||||||
ModalService
|
|
||||||
} from '../../../core/services/modal.service';
|
|
||||||
import { ModalShellComponent } from '../modal-shell/modal-shell.component';
|
import { ModalShellComponent } from '../modal-shell/modal-shell.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-modal-host',
|
selector: 'app-modal-host',
|
||||||
imports: [NgComponentOutlet, ModalShellComponent],
|
imports: [NgComponentOutlet, ModalShellComponent],
|
||||||
templateUrl: './modal-host.component.html',
|
templateUrl: './modal-host.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class ModalHostComponent {
|
export class ModalHostComponent {
|
||||||
private readonly modalService = inject(ModalService);
|
private readonly modalService = inject(ModalService);
|
||||||
@@ -42,9 +38,9 @@ export class ModalHostComponent {
|
|||||||
return Injector.create({
|
return Injector.create({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ModalRef, useValue: modal.ref },
|
{ 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 body = this.document.body;
|
||||||
const previousOverflow = body.style.overflow;
|
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';
|
body.style.overflow = 'hidden';
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -75,6 +83,7 @@ export class ModalHostComponent {
|
|||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
this.document.removeEventListener('keydown', onKeyDown);
|
this.document.removeEventListener('keydown', onKeyDown);
|
||||||
body.style.overflow = previousOverflow;
|
body.style.overflow = previousOverflow;
|
||||||
|
body.style.paddingRight = previousPaddingRight;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: rgba(16, 18, 22, 0.48);
|
background: rgba(16, 18, 22, 0.56);
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-shell__dialog {
|
.modal-shell__dialog {
|
||||||
|
|||||||
Reference in New Issue
Block a user