Skip to content

Commit 480611d

Browse files
committed
refactor(modal): signal inputs, host bindings, cleanup, tests
1 parent ccb56b4 commit 480611d

File tree

6 files changed

+197
-115
lines changed

6 files changed

+197
-115
lines changed

projects/coreui-angular/src/lib/modal/modal-dialog/modal-dialog.component.spec.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { ComponentRef } from '@angular/core';
23

34
import { ModalDialogComponent } from './modal-dialog.component';
45

56
describe('ModalDialogComponent', () => {
67
let component: ModalDialogComponent;
8+
let componentRef: ComponentRef<ModalDialogComponent>;
79
let fixture: ComponentFixture<ModalDialogComponent>;
810

911
beforeEach(async () => {
1012
await TestBed.configureTestingModule({
1113
imports: [ModalDialogComponent]
12-
})
13-
.compileComponents();
14+
}).compileComponents();
1415
});
1516

1617
beforeEach(() => {
1718
fixture = TestBed.createComponent(ModalDialogComponent);
1819
component = fixture.componentInstance;
20+
componentRef = fixture.componentRef;
1921
fixture.detectChanges();
2022
});
2123

@@ -26,4 +28,54 @@ describe('ModalDialogComponent', () => {
2628
it('should have css classes', () => {
2729
expect(fixture.nativeElement).toHaveClass('modal-dialog');
2830
});
31+
32+
it('should have css classes for alignment prop', () => {
33+
componentRef.setInput('alignment', 'center');
34+
fixture.detectChanges();
35+
expect(fixture.nativeElement).toHaveClass('modal-dialog-centered');
36+
componentRef.setInput('alignment', 'top');
37+
fixture.detectChanges();
38+
expect(fixture.nativeElement).not.toHaveClass('modal-dialog-centered');
39+
expect(fixture.nativeElement).toHaveClass('modal-dialog');
40+
});
41+
42+
it('should have css classes for fullscreen prop', () => {
43+
componentRef.setInput('fullscreen', true);
44+
fixture.detectChanges();
45+
expect(fixture.nativeElement).toHaveClass('modal-fullscreen');
46+
for (const size of ['sm', 'md', 'lg', 'xl', 'xxl']) {
47+
componentRef.setInput('fullscreen', size);
48+
fixture.detectChanges();
49+
expect(fixture.nativeElement).toHaveClass(`modal-fullscreen-${size}-down`);
50+
}
51+
componentRef.setInput('fullscreen', false);
52+
fixture.detectChanges();
53+
expect(fixture.nativeElement).not.toHaveClass('modal-fullscreen');
54+
expect(fixture.nativeElement).toHaveClass('modal-dialog');
55+
});
56+
57+
it('should have css classes for scrollable prop', () => {
58+
expect(fixture.nativeElement).not.toHaveClass('modal-dialog-scrollable');
59+
componentRef.setInput('scrollable', true);
60+
fixture.detectChanges();
61+
expect(fixture.nativeElement).toHaveClass('modal-dialog-scrollable');
62+
componentRef.setInput('scrollable', false);
63+
fixture.detectChanges();
64+
expect(fixture.nativeElement).not.toHaveClass('modal-dialog-scrollable');
65+
expect(fixture.nativeElement).toHaveClass('modal-dialog');
66+
});
67+
68+
it('should have css classes for size prop', () => {
69+
for (const size of ['sm', 'lg', 'xl']) {
70+
componentRef.setInput('size', size);
71+
fixture.detectChanges();
72+
expect(fixture.nativeElement).toHaveClass(`modal-${size}`);
73+
}
74+
componentRef.setInput('size', undefined);
75+
fixture.detectChanges();
76+
expect(fixture.nativeElement).not.toHaveClass('modal-sm');
77+
expect(fixture.nativeElement).not.toHaveClass('modal-lg');
78+
expect(fixture.nativeElement).not.toHaveClass('modal-xl');
79+
expect(fixture.nativeElement).toHaveClass('modal-dialog');
80+
});
2981
});
Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,48 @@
1-
import { Component, HostBinding, Input } from '@angular/core';
1+
import { booleanAttribute, Component, computed, input } from '@angular/core';
22

33
@Component({
44
selector: 'c-modal-dialog',
55
template: '<ng-content />',
66
styleUrls: ['./modal-dialog.component.scss'],
7-
host: { class: 'modal-dialog' }
7+
host: { class: 'modal-dialog', '[class]': 'hostClasses()' }
88
})
99
export class ModalDialogComponent {
1010
/**
1111
* Align the modal in the center or top of the screen.
12-
* @type {'top' | 'center'}
12+
* @default undefined
1313
*/
14-
@Input() alignment?: 'top' | 'center';
14+
readonly alignment = input<'top' | 'center'>();
15+
1516
/**
1617
* Set modal to covers the entire user viewport.
17-
* @type {boolean | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'}
18+
* @return {boolean | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'}
1819
*/
19-
@Input() fullscreen?: boolean | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
20+
readonly fullscreen = input<boolean | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'>();
21+
2022
/**
2123
* Does the modal dialog itself scroll, or does the whole dialog scroll within the window.
22-
* @type boolean
24+
* @default false
25+
* @return {boolean}
2326
*/
24-
@Input() scrollable?: boolean;
27+
readonly scrollable = input(false, { transform: booleanAttribute });
28+
2529
/**
2630
* Size the component small, large, or extra large.
31+
* @default undefined
32+
* @return {'sm' | 'lg' | 'xl'}
2733
*/
28-
@Input() size?: 'sm' | 'lg' | 'xl';
34+
readonly size = input<'sm' | 'lg' | 'xl'>();
2935

30-
@HostBinding('class')
31-
get hostClasses(): any {
36+
readonly hostClasses = computed(() => {
37+
const fullscreen = this.fullscreen();
38+
const size = this.size();
3239
return {
3340
'modal-dialog': true,
34-
'modal-dialog-centered': this.alignment === 'center',
35-
'modal-fullscreen': this.fullscreen === true,
36-
[`modal-fullscreen-${this.fullscreen}-down`]: this.fullscreen,
37-
'modal-dialog-scrollable': this.scrollable,
38-
[`modal-${this.size}`]: this.size
41+
'modal-dialog-centered': this.alignment() === 'center',
42+
'modal-fullscreen': fullscreen === true,
43+
[`modal-fullscreen-${fullscreen}-down`]: typeof fullscreen === 'string',
44+
'modal-dialog-scrollable': this.scrollable(),
45+
[`modal-${size}`]: !!size
3946
};
40-
}
47+
});
4148
}

projects/coreui-angular/src/lib/modal/modal-dismiss/modal-toggle.directive.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Directive, HostListener, inject, Input } from '@angular/core';
1+
import { Directive, HostListener, inject, input } from '@angular/core';
22

33
import { ModalService } from '../modal.service';
44

@@ -10,12 +10,13 @@ export class ModalToggleDirective {
1010

1111
/**
1212
* Html id attr of modal to dismiss.
13+
* @default undefined
1314
*/
14-
@Input('cModalToggle') id: string | undefined;
15+
readonly toggle = input<string>(undefined, { alias: 'cModalToggle' });
1516

1617
@HostListener('click', ['$event'])
1718
dismiss($event: any): void {
1819
$event.preventDefault();
19-
this.#modalService.toggle({ show: 'toggle', id: this.id });
20+
this.#modalService.toggle({ show: 'toggle', id: this.toggle() });
2021
}
2122
}

projects/coreui-angular/src/lib/modal/modal.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ export interface IModalAction {
1212
providedIn: 'root'
1313
})
1414
export class ModalService {
15-
private modalState = new Subject<any>();
16-
modalState$ = this.modalState.asObservable();
15+
readonly #modalState = new Subject<any>();
16+
readonly modalState$ = this.#modalState.asObservable();
1717

1818
toggle(action: IModalAction): void {
19-
this.modalState.next(action);
19+
this.#modalState.next(action);
2020
}
2121
}

projects/coreui-angular/src/lib/modal/modal/modal.component.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<c-modal-dialog
2-
[alignment]="alignment"
3-
[fullscreen]="fullscreen"
4-
[scrollable]="scrollable"
5-
[size]="size">
2+
[alignment]="alignment()"
3+
[fullscreen]="fullscreen()"
4+
[scrollable]="scrollable()"
5+
[size]="size()">
66
<c-modal-content>
77
<div [cdkTrapFocus]="visible" [cdkTrapFocusAutoCapture]="visible" style="display: contents;" #modalContentRef>
88
<ng-content />

0 commit comments

Comments
 (0)