Skip to content

Commit

Permalink
Added alert and alerts components to preview library along with assoc…
Browse files Browse the repository at this point in the history
…iated alert service
  • Loading branch information
jdwillmsen committed Nov 4, 2023
1 parent 3b4d0a0 commit a087594
Show file tree
Hide file tree
Showing 15 changed files with 1,428 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-ct.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
parallel-commands: |
npx nx-cloud record -- npx nx format:check
parallel-commands-on-agents: |
npx nx run-many --target=component-test --parallel=3 --skipNxCache
npx nx run-many --target=component-test --parallel=2 --skipNxCache
agents:
name: Nx Cloud - Agents
Expand Down
3 changes: 3 additions & 0 deletions apps/usersrole-nx/src/styles/_all-themes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
as palette;
@use '../../../../libs/usersrole-nx-app/theme/src/lib/components/create-palette/create-palette-theme.component'
as create-palette;
@use '../../../../libs/usersrole-nx-app/preview/src/lib/components/alert/alert-theme.component'
as alert;
@use '../styles/components/button/variants' as button-variants;

@mixin all-component-themes($theme) {
@include button-variants.theme($theme);
@include navigation-item.theme($theme);
@include palette.theme($theme);
@include create-palette.theme($theme);
@include alert.theme($theme);
@include mat.all-component-themes($theme);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@use 'sass:map';
@use '@angular/material' as mat;

@mixin color($theme) {
$color-config: mat.get-color-config($theme);
$primary-palette: map.get($color-config, 'primary');
$accent-palette: map.get($color-config, 'accent');
$warn-palette: map.get($theme, 'warn');
$error-palette: map.get($theme, 'error');
$success-palette: map.get($theme, 'success');
$info-palette: map.get($theme, 'info');

$primary-color: mat.get-color-from-palette($primary-palette, default);
$accent-color: mat.get-color-from-palette($accent-palette, default);
$warn-color: mat.get-color-from-palette($warn-palette, default);
$error-color: mat.get-color-from-palette($error-palette, default);
$success-color: mat.get-color-from-palette($success-palette, default);
$info-color: mat.get-color-from-palette($info-palette, default);

$primary-color-contrast: mat.get-color-from-palette(
$primary-palette,
default-contrast
);
$accent-color-contrast: mat.get-color-from-palette(
$accent-palette,
default-contrast
);
$warn-color-contrast: mat.get-color-from-palette(
$warn-palette,
default-contrast
);
$error-color-contrast: mat.get-color-from-palette(
$error-palette,
default-contrast
);
$success-color-contrast: mat.get-color-from-palette(
$success-palette,
default-contrast
);
$info-color-contrast: mat.get-color-from-palette(
$info-palette,
default-contrast
);

.alert {
@include _variant('primary', $primary-color, $primary-color-contrast);
@include _variant('accent', $accent-color, $accent-color-contrast);
@include _variant('warn', $warn-color, $warn-color-contrast);
@include _variant('error', $error-color, $error-color-contrast);
@include _variant('success', $success-color, $success-color-contrast);
@include _variant('info', $info-color, $info-color-contrast);
}
}

@mixin typography($theme) {
$typography-config: mat.get-typography-config($theme);
}

@mixin theme($theme) {
$color-config: mat.get-color-config($theme);

@if $color-config != null {
@include color($theme);
}

$typography-config: mat.get-typography-config($theme);

@if $typography-config != null {
@include typography($theme);
}
}

@mixin _variant($type, $color, $color-contrast) {
&.#{$type} {
background-color: color-mix(in srgb, $color 25%, transparent);
color: $color;

&.filled {
background-color: $color;
color: $color-contrast;
}

&.outlined {
background-color: transparent;
color: $color;
border: 1px solid $color;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { AlertComponent } from './alert.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

describe(AlertComponent.name, () => {
beforeEach(() => {
TestBed.overrideComponent(AlertComponent, {
TestBed.configureTestingModule({
imports: [BrowserAnimationsModule],
}).overrideComponent(AlertComponent, {
add: {
imports: [],
providers: [],
Expand All @@ -14,4 +17,89 @@ describe(AlertComponent.name, () => {
it('renders', () => {
cy.mount(AlertComponent);
});

it('should be setup properly with standard properties', () => {
cy.mount(AlertComponent, {
componentProperties: {
alerts: [
{
id: 'default-alert',
message: 'Test message',
icon: 'bug_report',
closeButton: true,
},
],
},
});
cy.getByCy('alert').should('be.visible');
cy.getByCy('message')
.should('be.visible')
.and('contain.text', 'Test message');
cy.getByCy('icon').should('be.visible');
cy.getByCy('close-button').should('be.visible');
});

it('should be setup properly multiple alert properties', () => {
cy.mount(AlertComponent, {
componentProperties: {
alerts: [
{
id: 'default-alert',
message: 'Test message 1',
icon: 'bug_report',
closeButton: true,
},
{
id: 'default-alert',
message: 'Test message 2',
icon: 'bug_report',
closeButton: true,
},
{
id: 'default-alert',
message: 'Test message 3',
icon: 'bug_report',
closeButton: true,
},
],
},
});
cy.getByCy('icon').should('have.length', '3').and('be.visible');
cy.getByCy('message').should('have.length', '3').and('be.visible');
cy.getByCy('close-button').should('have.length', '3').and('be.visible');
});

it('should be setup properly with no icon and close properties', () => {
cy.mount(AlertComponent, {
componentProperties: {
alerts: [
{
id: 'default-alert',
message: 'Test message',
},
],
},
});
cy.getByCy('alert').should('be.visible');
cy.getByCy('message').should('be.visible');
cy.getByCy('icon').should('not.be.visible');
cy.getByCy('close-button').should('not.be.visible');
});

it('should be able to close alert', () => {
cy.mount(AlertComponent, {
componentProperties: {
alerts: [
{
id: 'default-alert',
message: 'Test message',
icon: 'bug_report',
closeButton: true,
},
],
},
});
cy.getByCy('close-button').click();
cy.getByCy('alert').should('not.exist');
});
});
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
<p>alert works!</p>
<div
*ngFor="let alert of alerts"
[@fade]="{ value: fade, params: { fadeTime: fadeTime } }"
class="{{ cssClass(alert) }}"
data-cy="alert"
>
<mat-icon *ngIf="alert.icon" class="icon" data-cy="icon">
{{ alert.icon }}
</mat-icon>
<div *ngIf="!alert.icon" data-cy="icon"></div>
<div [innerHTML]="alert.message" class="message" data-cy="message"></div>
<button
(click)="removeAlert(alert)"
*ngIf="alert.closeButton"
data-cy="close-button"
mat-icon-button
type="button"
>
<mat-icon>close</mat-icon>
</button>
<div *ngIf="!alert.closeButton" data-cy="close-button"></div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.alert {
display: grid;
align-items: center;
grid-template-columns: auto 1fr auto;
padding: 4px;
margin: 4px;
border-radius: 4px;

.icon {
padding: 4px 0 4px 4px;
}

.message {
padding: 8px;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Component, Input } from '@angular/core';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { animate, style, transition, trigger } from '@angular/animations';
import { Alert, AlertVariants } from '../../models/alert.model';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { NavigationStart, Router } from '@angular/router';
import { AlertService } from '../../services/alert/alert.service';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';

@Component({
selector: 'usersrole-nx-alert',
standalone: true,
imports: [CommonModule],
imports: [CommonModule, MatIconModule, MatButtonModule],
templateUrl: './alert.component.html',
styleUrls: ['./alert.component.scss'],
animations: [
Expand All @@ -20,15 +23,81 @@ import { Router } from '@angular/router';
]),
],
})
export class AlertComponent {
export class AlertComponent implements OnInit, OnDestroy {
@Input() id = 'default-alert';
@Input() fade = false;
@Input() fadeTime = 500;
@Input() variant: AlertVariants = 'default';
alerts: Alert[] = [];
@Input() alerts: Alert[] = [];
alertSubscription!: Subscription;
routeSubscription!: Subscription;
autoCloseTimeout = 3000;

// constructor(private router: Router, private alertService: AlertService) {}
constructor(private router: Router, private alertService: AlertService) {}

ngOnInit() {
this.alertSubscription = this.alertService
.onAlert(this.id)
.subscribe((alert) => {
if (!alert?.message) {
this.alerts = this.alerts.filter(
(alert) => alert.keepAfterRouteChange
);
this.alerts.forEach((alert) => delete alert.keepAfterRouteChange);
return;
}
if (alert.maxSize && alert.maxSize > 0) {
while (this.alerts.length >= alert.maxSize) {
this.alerts.shift();
}
this.alerts.push(alert);
} else {
this.alerts.push(alert);
}
if (alert?.autoClose) {
if (alert.autoCloseTimeout && alert.autoCloseTimeout > 0) {
this.autoCloseTimeout = alert.autoCloseTimeout;
}
setTimeout(() => this.removeAlert(alert), this.autoCloseTimeout);
}
});

this.routeSubscription = this.router.events.subscribe((event) => {
if (event instanceof NavigationStart) {
this.alertService.clear(this.id);
}
});
}

ngOnDestroy() {
this.alertSubscription.unsubscribe();
this.routeSubscription.unsubscribe();
}

removeAlert(alert: Alert) {
if (!this.alerts.includes(alert)) return;

const timeout = this.fade ? this.fadeTime : 0;
alert.fade = this.fade;

setTimeout(() => {
this.alerts = this.alerts.filter((alt) => alt !== alert);
}, timeout);
}

cssClass(alert: Alert) {
if (!alert) return;

const classes = ['alert'];

if (alert.type !== undefined) {
classes.push(alert.type);
}

if (this.variant !== 'default') {
classes.push(this.variant);
}

return classes.join(' ');
}
}
Loading

0 comments on commit a087594

Please sign in to comment.