Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 34007f4

Browse filesBrowse files
committed
refactor(alert): signal inputs, host bindings, cleanup, tests
1 parent fb32db2 commit 34007f4
Copy full SHA for 34007f4

File tree

Expand file treeCollapse file tree

3 files changed

+118
-90
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+118
-90
lines changed

‎projects/coreui-angular/src/lib/alert/alert.component.html

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/alert/alert.component.html
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
@if (visible || !hide) {
2-
@if (dismissible) {
3-
<ng-container *ngTemplateOutlet="templates?.alertButtonCloseTemplate || defaultAlertButtonCloseTemplate" />
1+
@if (visible || !hide()) {
2+
@if (dismissible()) {
3+
<ng-container *ngTemplateOutlet="templates()?.['alertButtonCloseTemplate'] || defaultAlertButtonCloseTemplate" />
44
}
55
<ng-content />
66
}

‎projects/coreui-angular/src/lib/alert/alert.component.spec.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/alert/alert.component.spec.ts
+30-4Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,55 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
22
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
33

44
import { AlertComponent } from './alert.component';
5+
import { ComponentRef } from '@angular/core';
56

67
describe('AlertComponent', () => {
78
let component: AlertComponent;
9+
let componentRef: ComponentRef<AlertComponent>;
810
let fixture: ComponentFixture<AlertComponent>;
911

1012
beforeEach(waitForAsync(() => {
1113
TestBed.configureTestingModule({
12-
imports: [BrowserAnimationsModule, AlertComponent]
13-
})
14-
.compileComponents();
14+
imports: [BrowserAnimationsModule, AlertComponent, BrowserAnimationsModule]
15+
}).compileComponents();
1516
}));
1617

1718
beforeEach(() => {
1819
fixture = TestBed.createComponent(AlertComponent);
1920
component = fixture.componentInstance;
21+
componentRef = fixture.componentRef;
2022
fixture.detectChanges();
2123
});
2224

2325
it('should create', () => {
2426
expect(component).toBeTruthy();
2527
});
2628

27-
it('should have css classes', () => {
29+
it('should have css classes and styles', () => {
2830
expect(fixture.nativeElement).toHaveClass('alert');
31+
expect(fixture.nativeElement).toHaveClass('alert-primary');
32+
expect(fixture.nativeElement).toHaveClass('show');
33+
expect(fixture.nativeElement.style.opacity).toBe('1');
34+
componentRef.setInput('visible', false);
35+
componentRef.setInput('color', 'danger');
36+
fixture.detectChanges();
37+
expect(fixture.nativeElement).toHaveClass('alert-danger');
38+
expect(fixture.nativeElement.style.opacity).toBe('0');
39+
expect(fixture.nativeElement.style.height).toBe('0px');
40+
componentRef.setInput('dismissible', true);
41+
componentRef.setInput('fade', true);
42+
componentRef.setInput('variant', 'solid');
43+
componentRef.setInput('visible', true);
44+
fixture.detectChanges();
45+
expect(fixture.nativeElement).toHaveClass('alert-dismissible');
46+
expect(fixture.nativeElement).toHaveClass('fade');
47+
expect(fixture.nativeElement).not.toHaveClass('alert-danger');
48+
expect(fixture.nativeElement).toHaveClass('bg-danger');
49+
expect(fixture.nativeElement).toHaveClass('text-white');
50+
expect(fixture.nativeElement.style).toHaveSize(0);
51+
});
52+
53+
it('should have attributes', () => {
54+
expect(fixture.nativeElement.getAttribute('role')).toBe('alert');
2955
});
3056
});

‎projects/coreui-angular/src/lib/alert/alert.component.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/alert/alert.component.ts
+85-83Lines changed: 85 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import {
2-
AfterContentInit,
32
booleanAttribute,
43
Component,
5-
ContentChildren,
6-
EventEmitter,
7-
HostBinding,
8-
HostListener,
9-
Input,
10-
Output,
11-
QueryList
4+
computed,
5+
contentChildren,
6+
effect,
7+
input,
8+
output,
9+
signal,
10+
TemplateRef
1211
} from '@angular/core';
1312
import { NgTemplateOutlet } from '@angular/common';
1413
import { animate, AnimationEvent, state, style, transition, trigger } from '@angular/animations';
@@ -17,139 +16,142 @@ import { Colors } from '../coreui.types';
1716
import { TemplateIdDirective } from '../shared';
1817
import { ButtonCloseDirective } from '../button';
1918

20-
type AnimateType = ('hide' | 'show');
19+
type AnimateType = 'hide' | 'show';
2120

2221
@Component({
23-
selector: 'c-alert',
24-
templateUrl: './alert.component.html',
25-
styleUrls: ['./alert.component.scss'],
26-
exportAs: 'cAlert',
27-
imports: [NgTemplateOutlet, ButtonCloseDirective],
28-
animations: [
29-
trigger('fadeInOut', [
30-
state('show', style({ opacity: 1, height: '*', padding: '*', border: '*', margin: '*' })),
31-
state('hide', style({ opacity: 0, height: 0, padding: 0, border: 0, margin: 0 })),
32-
state('void', style({ opacity: 0, height: 0, padding: 0, border: 0, margin: 0 })),
33-
transition('show => hide', [
34-
animate('.3s ease-out')
35-
]),
36-
transition('hide => show', [
37-
animate('.3s ease-in')
38-
]),
39-
transition('show => void', [
40-
animate('.3s ease-out')
41-
]),
42-
transition('void => show', [
43-
animate('.3s ease-in')
44-
])
45-
])
46-
]
22+
selector: 'c-alert',
23+
templateUrl: './alert.component.html',
24+
styleUrls: ['./alert.component.scss'],
25+
exportAs: 'cAlert',
26+
imports: [NgTemplateOutlet, ButtonCloseDirective],
27+
animations: [
28+
trigger('fadeInOut', [
29+
state('show', style({ opacity: 1, height: '*', padding: '*', border: '*', margin: '*' })),
30+
state('hide', style({ opacity: 0, height: 0, padding: 0, border: 0, margin: 0 })),
31+
state('void', style({ opacity: 0, height: 0, padding: 0, border: 0, margin: 0 })),
32+
transition('show => hide', [animate('.3s ease-out')]),
33+
transition('hide => show', [animate('.3s ease-in')]),
34+
transition('show => void', [animate('.3s ease-out')]),
35+
transition('void => show', [animate('.3s ease-in')])
36+
])
37+
],
38+
host: {
39+
'[@.disabled]': '!fade()',
40+
'[@fadeInOut]': 'animateType',
41+
'[attr.role]': 'role()',
42+
'[class]': 'hostClasses()',
43+
'(@fadeInOut.start)': 'onAnimationStart($event)',
44+
'(@fadeInOut.done)': 'onAnimationDone($event)'
45+
}
4746
})
48-
export class AlertComponent implements AfterContentInit {
49-
50-
hide!: boolean;
47+
export class AlertComponent {
5148
/**
5249
* Sets the color context of the component to one of CoreUI’s themed colors.
53-
*
54-
* @type Colors
50+
* @return Colors
5551
* @default 'primary'
5652
*/
57-
@Input() color: Colors = 'primary';
53+
readonly color = input<Colors>('primary');
54+
5855
/**
5956
* Default role for alert. [docs]
60-
* @type string
57+
* @return string
6158
* @default 'alert'
6259
*/
63-
@HostBinding('attr.role')
64-
@Input() role = 'alert';
60+
readonly role = input('alert');
61+
6562
/**
6663
* Set the alert variant to a solid.
67-
* @type string
68-
*/
69-
@Input() variant?: 'solid' | string;
70-
/**
71-
* Event triggered on the alert dismiss.
64+
* @return string
7265
*/
73-
@Output() visibleChange: EventEmitter<boolean> = new EventEmitter();
74-
templates: any = {};
75-
@ContentChildren(TemplateIdDirective, { descendants: true }) contentTemplates!: QueryList<TemplateIdDirective>;
66+
readonly variant = input<'solid'>();
7667

7768
/**
7869
* Optionally adds a close button to alert and allow it to self dismiss.
79-
* @type boolean
70+
* @return boolean
8071
* @default false
8172
*/
82-
@Input({ transform: booleanAttribute }) dismissible: boolean = false;
73+
readonly dismissible = input(false, { transform: booleanAttribute });
8374

8475
/**
8576
* Adds animation for dismissible alert.
86-
* @type boolean
77+
* @return boolean
8778
*/
88-
@Input({ transform: booleanAttribute }) fade: boolean = false;
79+
readonly fade = input(false, { transform: booleanAttribute });
8980

9081
/**
9182
* Toggle the visibility of alert component.
92-
* @type boolean
83+
* @return boolean
9384
*/
94-
@Input({ transform: booleanAttribute })
85+
readonly visibleInput = input(true, { transform: booleanAttribute, alias: 'visible' });
86+
87+
readonly #visibleInputEffect = effect(() => {
88+
this.visible = this.visibleInput();
89+
});
90+
9591
set visible(value: boolean) {
9692
if (this.#visible !== value) {
9793
this.#visible = value;
9894
this.visibleChange.emit(value);
9995
}
100-
};
96+
}
10197

10298
get visible() {
10399
return this.#visible;
104100
}
105101

106102
#visible: boolean = true;
107103

108-
@HostBinding('@.disabled')
109-
get animationDisabled(): boolean {
110-
return !this.fade;
111-
}
104+
readonly hide = signal<boolean>(false);
105+
106+
/**
107+
* Event triggered on the alert dismiss.
108+
*/
109+
readonly visibleChange = output<boolean>();
110+
111+
readonly contentTemplates = contentChildren(TemplateIdDirective, { descendants: true });
112+
113+
readonly templates = computed(() => {
114+
return this.contentTemplates().reduce(
115+
(acc, child) => {
116+
acc[child.id] = child.templateRef;
117+
return acc;
118+
},
119+
{} as Record<string, TemplateRef<any>>
120+
);
121+
});
112122

113-
@HostBinding('@fadeInOut')
114123
get animateType(): AnimateType {
115124
return this.visible ? 'show' : 'hide';
116125
}
117126

118-
@HostBinding('class')
119-
get hostClasses(): any {
127+
readonly hostClasses = computed(() => {
128+
const color = this.color();
129+
const variant = this.variant();
120130
return {
121131
alert: true,
122-
'alert-dismissible': this.dismissible,
123-
fade: this.fade,
124-
show: !this.hide,
125-
[`alert-${this.color}`]: !!this.color && this.variant !== 'solid',
126-
[`bg-${this.color}`]: !!this.color && this.variant === 'solid',
127-
'text-white': !!this.color && this.variant === 'solid'
128-
};
129-
}
132+
'alert-dismissible': this.dismissible(),
133+
fade: this.fade(),
134+
show: !this.hide(),
135+
[`alert-${color}`]: !!color && variant !== 'solid',
136+
[`bg-${color}`]: !!color && variant === 'solid',
137+
'text-white': !!color && variant === 'solid'
138+
} as Record<string, boolean>;
139+
});
130140

131-
@HostListener('@fadeInOut.start', ['$event'])
132141
onAnimationStart($event: AnimationEvent): void {
133142
this.onAnimationEvent($event);
134143
}
135144

136-
@HostListener('@fadeInOut.done', ['$event'])
137145
onAnimationDone($event: AnimationEvent): void {
138146
this.onAnimationEvent($event);
139147
}
140148

141-
ngAfterContentInit(): void {
142-
this.contentTemplates.forEach((child: TemplateIdDirective) => {
143-
this.templates[child.id] = child.templateRef;
144-
});
145-
}
146-
147149
onAnimationEvent(event: AnimationEvent): void {
148-
this.hide = event.phaseName === 'start' && event.toState === 'show';
150+
this.hide.set(event.phaseName === 'start' && event.toState === 'show');
149151
if (event.phaseName === 'done') {
150-
this.hide = (event.toState === 'hide' || event.toState === 'void');
152+
this.hide.set(event.toState === 'hide' || event.toState === 'void');
151153
if (event.toState === 'show') {
152-
this.hide = false;
154+
this.hide.set(false);
153155
}
154156
}
155157
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.