Skip to content

Commit

Permalink
refactor: more updates to use signals
Browse files Browse the repository at this point in the history
  • Loading branch information
jrassa committed May 30, 2024
1 parent d4b0149 commit b534f3b
Show file tree
Hide file tree
Showing 41 changed files with 417 additions and 431 deletions.
10 changes: 5 additions & 5 deletions src/app/common/breadcrumb/breadcrumb.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { Breadcrumb, BreadcrumbService } from './breadcrumb.service';
imports: [RouterLink]
})
export class BreadcrumbComponent {
readonly #route = inject(ActivatedRoute);
readonly #router = inject(Router);

@Input({ required: true })
set homeBreadcrumb(hb: Breadcrumb) {
this._homeBreadcrumb = hb;
Expand All @@ -25,11 +28,8 @@ export class BreadcrumbComponent {

breadcrumbs: Breadcrumb[] = [];

private route = inject(ActivatedRoute);
private router = inject(Router);

constructor() {
const navEnd$: Observable<Event> = this.router.events.pipe(
const navEnd$: Observable<Event> = this.#router.events.pipe(
filter((event) => event instanceof NavigationEnd)
);
merge(navEnd$, this.homeBreadcrumbChanged$)
Expand All @@ -39,7 +39,7 @@ export class BreadcrumbComponent {
this.breadcrumbs = [this._homeBreadcrumb];
this.breadcrumbs = this.breadcrumbs.concat(
BreadcrumbService.getBreadcrumbs(
this.route.root.snapshot,
this.#route.root.snapshot,
this._homeBreadcrumb.url
)
);
Expand Down
7 changes: 2 additions & 5 deletions src/app/common/flyout/flyout.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
<section
class="flyout flyout-{{ placement }} d-flex text-nowrap"
[ngClass]="{ 'flyout-open': isOpen }"
>
<section class="flyout flyout-{{ placement() }} d-flex text-nowrap" [class.flyout-open]="isOpen()">
<button
class="btn btn-primary flyout-btn d-flex align-items-center"
data-inline="true"
(click)="toggle()"
>
{{ label }}
{{ label() }}
<span class="fa-solid fa-lg fa-angle-down ms-2" data-inline="true"></span>
</button>

Expand Down
50 changes: 24 additions & 26 deletions src/app/common/flyout/flyout.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { NgClass } from '@angular/common';
import {
Component,
ContentChild,
ElementRef,
Input,
Renderer2,
ViewChild,
inject
contentChild,
inject,
input,
signal,
viewChild
} from '@angular/core';

@Component({
Expand All @@ -17,44 +18,41 @@ import {
imports: [NgClass]
})
export class FlyoutComponent {
@ViewChild('flyoutContentContainer') container?: ElementRef;
@ContentChild('flyoutContent') content?: ElementRef;
readonly #renderer = inject(Renderer2);

@Input()
label = '';
readonly container = viewChild.required<ElementRef>('flyoutContentContainer');
readonly content = contentChild.required<ElementRef>('flyoutContent');

@Input()
placement: 'left' | 'right' | 'top' | 'bottom' = 'right';
readonly label = input('');
readonly placement = input<'left' | 'right' | 'top' | 'bottom'>('right');

isOpen = false;

private renderer = inject(Renderer2);
readonly isOpen = signal(false);

toggle() {
if (this.content && this.container) {
if (this.placement === 'top' || this.placement === 'bottom') {
if (this.isOpen) {
this.renderer.setStyle(this.container.nativeElement, 'height', 0);
if (this.content() && this.container()) {
if (this.placement() === 'top' || this.placement() === 'bottom') {
if (this.isOpen()) {
this.#renderer.setStyle(this.container().nativeElement, 'height', 0);
} else {
this.renderer.setStyle(
this.container.nativeElement,
this.#renderer.setStyle(
this.container().nativeElement,
'height',
`${this.content.nativeElement.clientHeight}px`
`${this.content().nativeElement.clientHeight}px`
);
}
} else {
if (this.isOpen) {
this.renderer.setStyle(this.container.nativeElement, 'width', 0);
if (this.isOpen()) {
this.#renderer.setStyle(this.container().nativeElement, 'width', 0);
} else {
this.renderer.setStyle(
this.container.nativeElement,
this.#renderer.setStyle(
this.container().nativeElement,
'width',
`${this.content.nativeElement.clientWidth}px`
`${this.content().nativeElement.clientWidth}px`
);
}
}

this.isOpen = !this.isOpen;
this.isOpen.set(!this.isOpen());
}
}
}
13 changes: 6 additions & 7 deletions src/app/common/loading-overlay/loading-overlay.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
@if (isLoading) {
@if (isLoading()) {
<div class="overlay">
@if (isError) {
<notification notificationType="danger" showActions [message]="errorMessage">
@if (isError()) {
<notification notificationType="danger" showActions [message]="errorMessage()">
<ng-template #notificationActions>
<button class="btn btn-primary" (click)="handleRetry()">Retry</button>
<button class="btn btn-primary" (click)="retry.emit()">Retry</button>
</ng-template>
</notification>
}
@if (!isError) {
} @else {
<div class="overlay-spinner">
<loading-spinner [message]="message"> </loading-spinner>
<loading-spinner [message]="message()"> </loading-spinner>
</div>
}
</div>
Expand Down
18 changes: 9 additions & 9 deletions src/app/common/loading-overlay/loading-overlay.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

Check warning on line 2 in src/app/common/loading-overlay/loading-overlay.component.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'By' is defined but never used

Check warning on line 2 in src/app/common/loading-overlay/loading-overlay.component.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'By' is defined but never used

Check warning on line 2 in src/app/common/loading-overlay/loading-overlay.component.spec.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

'By' is defined but never used

import { LoadingOverlayComponent } from './loading-overlay.component';

describe('LoadingOverlayComponent', () => {
let fixture: ComponentFixture<LoadingOverlayComponent>;
let rootHTMLElement: HTMLElement;
let component: LoadingOverlayComponent;

beforeEach(() => {
const testbed = TestBed.configureTestingModule({
fixture = TestBed.configureTestingModule({
imports: [LoadingOverlayComponent]
});
}).createComponent(LoadingOverlayComponent);

fixture = testbed.createComponent(LoadingOverlayComponent);
component = fixture.componentInstance;
rootHTMLElement = fixture.debugElement.nativeElement;
fixture.detectChanges();
});

it('should display loading overlay and loading spinner', () => {
component.isLoading = true;
fixture.componentRef.setInput('isLoading', true);
fixture.detectChanges();
// expect(rootHTMLElement.innerHTML).toEqual('');
// expect(fixture.debugElement.query(By.css('.overlay'))).toBeDefined();
expect(rootHTMLElement.getElementsByClassName('overlay').length).toEqual(1);
expect(rootHTMLElement.getElementsByClassName('overlay-spinner').length).toEqual(1);
expect(rootHTMLElement.getElementsByClassName('alert').length).toEqual(0);
});

it('should display loading overlay and error message', () => {
component.isLoading = true;
component.isError = true;
fixture.componentRef.setInput('isLoading', true);
fixture.componentRef.setInput('isError', true);
fixture.detectChanges();
expect(rootHTMLElement.getElementsByClassName('overlay').length).toEqual(1);
expect(rootHTMLElement.getElementsByClassName('overlay-spinner').length).toEqual(0);
expect(rootHTMLElement.getElementsByClassName('alert').length).toEqual(1);
});

it('should not display loading overlay', () => {
component.isLoading = false;
fixture.componentRef.setInput('isLoading', false);
fixture.detectChanges();
expect(rootHTMLElement.getElementsByClassName('overlay').length).toEqual(0);
expect(rootHTMLElement.getElementsByClassName('overlay-spinner').length).toEqual(0);
Expand Down
27 changes: 8 additions & 19 deletions src/app/common/loading-overlay/loading-overlay.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';

import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
import { NotificationComponent } from '../notification/notification.component';
Expand All @@ -8,25 +8,14 @@ import { NotificationComponent } from '../notification/notification.component';
templateUrl: 'loading-overlay.component.html',
styleUrls: ['loading-overlay.component.scss'],
standalone: true,
imports: [NotificationComponent, LoadingSpinnerComponent]
imports: [NotificationComponent, LoadingSpinnerComponent],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LoadingOverlayComponent {
@Input()
message = 'Loading...';
readonly message = input('Loading...');
readonly isLoading = input(false);
readonly isError = input(false);
readonly errorMessage = input('');

@Input()
isLoading = false;

@Input()
isError = false;

@Input()
errorMessage = '';

@Output()
readonly retry = new EventEmitter();

handleRetry() {
this.retry.emit(true);
}
readonly retry = output();
}
10 changes: 5 additions & 5 deletions src/app/common/notification/notification.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div class="alert alert-{{ notificationType }} d-flex" [class.small]="small">
<div class="flex-grow-1" [class.action-text]="showActions">
{{ message }}
<div class="alert alert-{{ notificationType() }} d-flex" [class.small]="small()">
<div class="flex-grow-1" [class.action-text]="showActions()">
{{ message() }}
</div>
@if (showActions) {
@if (showActions() && actionTemplate()) {
<div class="table-actions d-flex">
<ng-container *ngTemplateOutlet="actionTemplate"></ng-container>
<ng-container *ngTemplateOutlet="actionTemplate() ?? null"></ng-container>
</div>
}
</div>
30 changes: 15 additions & 15 deletions src/app/common/notification/notification.component.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, Input, TemplateRef, booleanAttribute } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
TemplateRef,
booleanAttribute,
contentChild,
input
} from '@angular/core';

@Component({
selector: 'notification',
templateUrl: 'notification.component.html',
styleUrls: ['notification.component.scss'],
standalone: true,
imports: [NgTemplateOutlet]
imports: [NgTemplateOutlet],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotificationComponent {
@ContentChild('notificationActions', { static: true }) actionTemplate: TemplateRef<any> | null =
null;
readonly actionTemplate = contentChild<TemplateRef<any>>('notificationActions');

@Input()
notificationType: 'info' | 'success' | 'warning' | 'danger' = 'info';

@Input()
message = '';

@Input({ transform: booleanAttribute })
showActions = false;

@Input({ transform: booleanAttribute })
small = false;
readonly notificationType = input<'info' | 'success' | 'warning' | 'danger'>('info');
readonly message = input('');
readonly showActions = input(false, { transform: booleanAttribute });
readonly small = input(false, { transform: booleanAttribute });
}
8 changes: 4 additions & 4 deletions src/app/common/search-input/search-input.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
class="form-control"
type="text"
[(ngModel)]="search"
[placeholder]="placeholder"
[placeholder]="placeholder()"
(input)="onInput()"
(keyup)="onKeyup()"
/>
@if (search.length === 0) {
@if (search().length === 0) {
<span class="icon fa-solid fa-search"></span>
} @else {
<span class="icon fa-solid fa-times" (click)="clearSearch($event)"></span>
}
</div>
@if (!disableMinCountMessage && showMinCountMessage) {
@if (!disableMinCountMessage() && showMinCountMessage()) {
<div class="text-muted pt-2" [@minCount]>
<div class="pt-2">
Searches require a minimum of {{ minSearchCharacterCount }} characters.
Searches require a minimum of {{ minSearchCharacterCount() }} characters.
</div>
</div>
}
Loading

0 comments on commit b534f3b

Please sign in to comment.