Skip to content

Commit 362a1c4

Browse files
authored
feat(eng-8901): consolidate the osf banners into 1 component (CenterForOpenScience#416)
* chore(merge-conflicts): fixed merge conflicts * chore(merge-conflicts): fixed merge conflicts - 2
1 parent d2c87ec commit 362a1c4

20 files changed

Lines changed: 390 additions & 122 deletions
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { animate, style, transition, trigger } from '@angular/animations';
2+
3+
/**
4+
* Angular animation trigger for fading elements in and out.
5+
*
6+
* This trigger can be used with Angular structural directives like `*ngIf` or `@if`
7+
* to smoothly animate the appearance and disappearance of components or elements.
8+
*
9+
* ## Usage:
10+
*
11+
* In the component decorator:
12+
* ```ts
13+
* @Component({
14+
* selector: 'my-component',
15+
* templateUrl: './my.component.html',
16+
* animations: [fadeInOut]
17+
* })
18+
* export class MyComponent {}
19+
* ```
20+
*
21+
* In the template:
22+
* ```html
23+
* @if (show) {
24+
* <div @fadeInOut>
25+
* Fades in and out!
26+
* </div>
27+
* }
28+
* ```
29+
*
30+
* ## Transitions:
31+
* - **:enter** — Fades in from opacity `0` to `1` over `200ms`.
32+
* - **:leave** — Fades out from opacity `1` to `0` over `200ms`.
33+
*
34+
* @returns An Angular `AnimationTriggerMetadata` object used for component animations.
35+
*/
36+
export const fadeInOutAnimation = trigger('fadeInOut', [
37+
transition(':enter', [style({ opacity: 0 }), animate('200ms', style({ opacity: 1 }))]),
38+
transition(':leave', [animate('200ms', style({ opacity: 0 }))]),
39+
]);

src/app/core/components/breadcrumb/breadcrumb.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
77

88
import { BreadcrumbComponent } from './breadcrumb.component';
99

10-
describe('BreadcrumbComponent', () => {
10+
describe('Component: Breadcrumb', () => {
1111
let component: BreadcrumbComponent;
1212
let fixture: ComponentFixture<BreadcrumbComponent>;
1313

src/app/core/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
22
export { FooterComponent } from './footer/footer.component';
33
export { HeaderComponent } from './header/header.component';
4-
export { MaintenanceBannerComponent } from './maintenance-banner/maintenance-banner.component';
4+
export { MaintenanceBannerComponent } from './osf-banners/maintenance-banner/maintenance-banner.component';
55
export { PageNotFoundComponent } from './page-not-found/page-not-found.component';
66
export { RootComponent } from './root/root.component';
77
export { SidenavComponent } from './sidenav/sidenav.component';

src/app/core/components/maintenance-banner/maintenance-banner.component.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/app/core/components/maintenance-banner/maintenance-banner.component.html renamed to src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
@if (maintenance && !dismissed) {
1+
@if (maintenance() && !dismissed()) {
22
<p-message
33
styleClass="w-full"
4-
[severity]="maintenance.severity"
5-
[text]="maintenance.message"
4+
[severity]="maintenance()?.severity"
5+
[text]="maintenance()?.message"
66
[closable]="true"
77
(onClose)="dismiss()"
88
class="block p-3"
99
icon="pi pi-info-circle"
10+
@fadeInOut
1011
>
1112
</p-message>
1213
}

src/app/core/components/maintenance-banner/maintenance-banner.component.scss renamed to src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.scss

File renamed without changes.

src/app/core/components/maintenance-banner/maintenance-banner.component.spec.ts renamed to src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
1111

1212
import { MaintenanceBannerComponent } from './maintenance-banner.component';
1313

14-
describe('MaintenanceBannerComponent', () => {
14+
describe('Component: Maintenance Banner', () => {
1515
let fixture: ComponentFixture<MaintenanceBannerComponent>;
1616
let httpClient: { get: jest.Mock };
1717
let cookieService: jest.Mocked<CookieService>;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { CookieService } from 'ngx-cookie-service';
2+
3+
import { MessageModule } from 'primeng/message';
4+
5+
import { CommonModule } from '@angular/common';
6+
import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';
7+
8+
import { fadeInOutAnimation } from '@core/animations/fade.in-out.animation';
9+
10+
import { MaintenanceModel } from '../models/maintenance.model';
11+
import { MaintenanceService } from '../services/maintenance.service';
12+
13+
/**
14+
* A banner component that displays a scheduled maintenance message to users.
15+
*
16+
* This component checks a cookie to determine whether the user has previously dismissed
17+
* the banner. If not, it queries the maintenance status from the server and displays
18+
* the maintenance message if one is active.
19+
*
20+
* The component supports animation via `fadeInOutAnimation` and is optimized with `OnPush` change detection.
21+
*
22+
* @example
23+
* ```html
24+
* <osf-maintenance-banner />
25+
* ```
26+
*/
27+
@Component({
28+
selector: 'osf-maintenance-banner',
29+
imports: [CommonModule, MessageModule],
30+
templateUrl: './maintenance-banner.component.html',
31+
styleUrls: ['./maintenance-banner.component.scss'],
32+
animations: [fadeInOutAnimation],
33+
changeDetection: ChangeDetectionStrategy.OnPush,
34+
})
35+
export class MaintenanceBannerComponent implements OnInit {
36+
/**
37+
* Signal to track whether the user has dismissed the banner.
38+
*/
39+
dismissed = signal<boolean>(false);
40+
41+
/**
42+
* Signal that holds the current maintenance status fetched from the server.
43+
*/
44+
maintenance = signal<MaintenanceModel | null>(null);
45+
46+
/**
47+
* Service used to fetch maintenance status from the backend.
48+
*/
49+
private readonly maintenanceService = inject(MaintenanceService);
50+
51+
/**
52+
* Cookie service used to persist dismissal state in the browser.
53+
*/
54+
private readonly cookies = inject(CookieService);
55+
56+
/**
57+
* The cookie name used to store whether the user dismissed the banner.
58+
*/
59+
private readonly cookieName = 'osf-maintenance-dismissed';
60+
61+
/**
62+
* Duration (in hours) to persist the dismissal cookie.
63+
*/
64+
private readonly cookieDurationHours = 24;
65+
66+
/**
67+
* Lifecycle hook that initializes the component:
68+
* - Checks if dismissal cookie exists and sets `dismissed`
69+
* - If not dismissed, triggers a fetch of current maintenance status
70+
*/
71+
ngOnInit(): void {
72+
this.dismissed.set(this.cookies.check(this.cookieName));
73+
if (!this.dismissed()) {
74+
this.fetchMaintenanceStatus();
75+
}
76+
}
77+
78+
/**
79+
* Fetches the current maintenance status from the backend via the `MaintenanceService`
80+
* and sets it to the `maintenance` signal.
81+
*
82+
* If no maintenance is active, the signal remains `null`.
83+
*/
84+
private fetchMaintenanceStatus(): void {
85+
this.maintenanceService.fetchMaintenanceStatus().subscribe((maintenance: MaintenanceModel | null) => {
86+
this.maintenance.set(maintenance);
87+
});
88+
}
89+
90+
/**
91+
* Dismisses the banner:
92+
* - Sets a cookie to remember dismissal for 24 hours
93+
* - Updates the `dismissed` and `maintenance` signals
94+
*/
95+
dismiss(): void {
96+
this.cookies.set(this.cookieName, '1', this.cookieDurationHours, '/');
97+
this.dismissed.set(true);
98+
this.maintenance.set(null);
99+
}
100+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type MaintenanceSeverityType = 'info' | 'warn' | 'error';
2+
3+
export interface MaintenanceModel {
4+
level: number;
5+
message: string;
6+
start: string;
7+
end: string;
8+
severity?: MaintenanceSeverityType;
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<osf-maintenance-banner></osf-maintenance-banner>

0 commit comments

Comments
 (0)