Skip to content

Commit

Permalink
feat(design): add toast component (#2499)
Browse files Browse the repository at this point in the history
Co-authored-by: Damien Retzinger <[email protected]>
Co-authored-by: Peter Lauck <[email protected]>
  • Loading branch information
3 people committed Mar 5, 2024
1 parent 377c983 commit 3d960ef
Show file tree
Hide file tree
Showing 56 changed files with 2,091 additions and 2 deletions.
1 change: 1 addition & 0 deletions apps/design-land/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const appRoutes: Routes = [
{ path: 'quantity-field', loadChildren: () => import('./quantity-field/quantity-field.module').then(m => m.DesignLandQuantityFieldModule) },
{ path: 'sidebar', loadChildren: () => import('./sidebar/sidebar.module').then(m => m.DesignLandSidebarModule) },
{ path: 'radio', loadChildren: () => import('./radio/radio.module').then(m => m.DesignLandRadioModule) },
{ path: 'toast', loadChildren: () => import('./toast/toast.module').then(m => m.DesignLandToastModule) },
{ path: 'typography', loadChildren: () => import('./typography/typography.module').then(m => m.DesignLandTypographyModule) },
{ path: 'variables', loadChildren: () => import('./foundations/variables/variables.module').then(m => m.DesignLandVariablesModule) },
],
Expand Down
2 changes: 2 additions & 0 deletions apps/design-land/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { PAGINATOR_EXAMPLES } from '@daffodil/design/paginator/examples';
import { QUANTITY_FIELD_EXAMPLES } from '@daffodil/design/quantity-field/examples';
import { RADIO_EXAMPLES } from '@daffodil/design/radio/examples';
import { SIDEBAR_EXAMPLES } from '@daffodil/design/sidebar/examples';
import { TOAST_EXAMPLES } from '@daffodil/design/toast/examples';

import { createCustomElementFromExample } from './core/elements/create-element-from-example';

Expand Down Expand Up @@ -60,6 +61,7 @@ export class DesignLandAppComponent {
...IMAGE_EXAMPLES,
...INPUT_EXAMPLES,
...SIDEBAR_EXAMPLES,
...TOAST_EXAMPLES,
].map((componentExample) => createCustomElementFromExample(componentExample, injector))
.map((customElement) => {
// Register the custom element with the browser.
Expand Down
2 changes: 2 additions & 0 deletions apps/design-land/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { DaffButtonModule } from '@daffodil/design/button';
import { DaffLinkSetModule } from '@daffodil/design/link-set';
import { DaffNavbarModule } from '@daffodil/design/navbar';
import { DaffSidebarModule } from '@daffodil/design/sidebar';
import { DaffToastModule } from '@daffodil/design/toast';
import { DaffThemeSwitchButtonModule } from '@daffodil/theme-switch';

import { DesignLandAppRoutingModule } from './app-routing.module';
Expand All @@ -32,6 +33,7 @@ import { DesignLandTemplateModule } from './core/template/template.module';
FontAwesomeModule,
DesignLandNavModule,
DesignLandTemplateModule,
DaffToastModule,
],
declarations: [
DesignLandAppComponent,
Expand Down
21 changes: 21 additions & 0 deletions apps/design-land/src/app/toast/toast-routing-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import {
Routes,
RouterModule,
} from '@angular/router';

import { DesignLandToastComponent } from './toast.component';

export const toastRoutes: Routes = [
{ path: '', component: DesignLandToastComponent },
];

@NgModule({
imports: [
RouterModule.forChild(toastRoutes),
],
exports: [
RouterModule,
],
})
export class DesignLandToastRoutingModule {}
169 changes: 169 additions & 0 deletions apps/design-land/src/app/toast/toast.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<h1 daffArticleTitle>Toast</h1>
<p daffArticleLead>Toasts are small messages designed to mimic push notifications. They are used to provide users with application level information.</p>

<h2>Overview</h2>
<p>Toasts should be used to display temporary messages about actions or events that occured or in need of attention, with no relation to content on a page. For messaging that provide context in close promixity to a piece of content within a page, use the <a routerLink="/notification">Notification</a> component.</p>

<h3>Basic toast</h3>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="default-toast"></design-land-example-viewer-container>
</design-land-article-encapsulated>

<h3>Configurations</h3>
Toasts can be configured by using the <code>DaffToastService</code>.

The following is an example of a toast with a duration:

<pre><code>constructor(private toastService: DaffToastService) &lcub;&rcub;

open() &lcub;
this.toast = this.toastService.open(&lcub;
title: 'Update Complete',
message: 'This page has been updated to the newest version.',
&rcub;,
&lcub;
duration: 5000,
&rcub;);
&rcub;</code></pre>

The following is an example of a toast with actions:

<pre><code>constructor(private toastService: DaffToastService) &lcub;&rcub;

open() &lcub;
this.toast = this.toastService.open(&lcub;
title: 'Update Available',
message: 'A new version of this page is available.',
actions: [
&lcub; content: 'Update', color: 'theme-contrast', size: 'sm', eventEmitter: this.update &rcub;,
&lcub; content: 'Remind me later', type: 'flat', size: 'sm', eventEmitter: this.closeToast &rcub;,
],
&rcub;);
&rcub;</code></pre>

The following configurations are available in the <code>DaffToastService</code>:

<table>
<thead>
<tr>
<td>Property</td>
<Td>Type</Td>
<td>Description</td>
<td>Default</td>
</tr>
</thead>
<tbody>
<tr>
<td>title</td>
<td>string</td>
<td>A quick overview of the toast</td>
<td>--</td>
</tr>
<tr>
<td>message</td>
<td>string</td>
<td>Additional details about the message that should be limited to one or two sentences</td>
<td>--</td>
</tr>
<tr>
<td>actions</td>
<td><code>DaffToastAction</code></td>
<td>Adds a <code>daff-button</code> that allow users to perform an action related to the message. Actions should be limited to two buttons.</td>
<td>--</td>
</tr>
<tr>
<td>dismissible</td>
<td>boolean</td>
<td>Allows a toast to be dismissible via a close button</td>
<td><code>true</code></td>
</tr>
<tr>
<td>duration</td>
<td>number</td>
<td>The duration in milliseconds that a toast is visible before it's dismissed</td>
<td>5000</td>
</tr>
</tbody>
</table>

The <code>actions</code> configurations are based on the properties of the <code>DaffButtonComponent</code> (view <a routerLink="/button">Button Documentation</a>), with the addition of <code>data</code> and <code>eventEmitter</code>.

<h3>Dismissal</h3>
<p>A toast can be dismissed via a timed duration, a close button, or the <code>ESC</code> key.

<h5>Timed duration</h5>
<p>A toast with actions will persist until one of the actions have been interacted with, or is dismissed by the close button or the <code>ESC</code> key. Setting a duration should be avoided for toasts that have actions as users may need to interact with the actions.</p>

<h4>Toast with custom duration</h4>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="toast-with-custom-duration"></design-land-example-viewer-container>
</design-land-article-encapsulated>

<p>By default, a toast without actions will be dismissed after <code>5000ms</code>. This can be updated by setting <code>duration</code> through the <code>DaffToastService</code>.</p>

<h5>Close button</h5>
<p>The close button is shown by default but can be hidden by setting <code>dismissible: false</code> through the <code>DaffToastService</code>.</p>

<h5>Escape key</h5>
<p>A toast can be dismissed by using the <code>ESC</code> key if it has actions and is focus trapped.</p>

<h3>Stacking</h3>
<p>A maximum of three toasts can be shown at a time. Toasts are stacked vertically, with the most recent toast displayed on top.</p>

<h3>Statuses</h3>
<p>The status color of a toast can be updated by using the <code>status</code> property.</p>

<p>Supported statuses: <code>warn | danger | success</code></p>

<h4>Toast with statuses</h4>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="toast-status"></design-land-example-viewer-container>
</design-land-article-encapsulated>

<h3>Positions</h3>

<table>
<thead>
<tr>
<th>Property</th>
<th>Value</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td>vertical</td>
<td><code>top | bottom</code></td>
<td><code>top</code></td>
</tr>
<tr>
<td>horizontal</td>
<td><code>left | center | right</code></td>
<td><code>right</code></td>
</tr>
</tbody>
</table>

<p>To change the horizontal and vertical position of a toast, add the <code>provideDaffToastOptions</code> dependency key to the <code>providers</code> key in the module as shown below:</p>

<pre><code>providers: [
provideDaffToastOptions(&lcub;
position: &lcub;
vertical: 'bottom',
horizontal: 'center',
&rcub;,
useParent: false,
&rcub;),
]</code></pre>

<p>The position of a toast on a mobile device will always be on the bottom center.</p>

<h4>Toast with configurable positions</h4>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="toast-positions"></design-land-example-viewer-container>
</design-land-article-encapsulated>

<h3>Accessibility</h3>
<p>By default, toasts use a <code>role="status"</code> to announce messages. It's the equivalent of <code>aria-live="polite"</code>, which does not interrupt a user's current activity and waits until they are idle to make the announcement. When a toast has actions, a <code>role="alertdialog"</code> is used. The toast will also be focus trapped, and the focus immediately moves to the actions.</p>

<p>Avoid setting a duration on toasts with actions because they will disappear automatically, making it difficult for users to interact with the actions.</p>
Empty file.
29 changes: 29 additions & 0 deletions apps/design-land/src/app/toast/toast.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
ComponentFixture,
TestBed,
waitForAsync,
} from '@angular/core/testing';

import { DesignLandToastComponent } from './toast.component';

describe('DesignLandToastComponent', () => {
let component: DesignLandToastComponent;
let fixture: ComponentFixture<DesignLandToastComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ DesignLandToastComponent ],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(DesignLandToastComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
8 changes: 8 additions & 0 deletions apps/design-land/src/app/toast/toast.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'design-land-toast',
templateUrl: './toast.component.html',
styleUrls: ['./toast.component.scss'],
})
export class DesignLandToastComponent {}
28 changes: 28 additions & 0 deletions apps/design-land/src/app/toast/toast.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { DaffArticleModule } from '@daffodil/design/article';
import { DaffToastModule } from '@daffodil/design/toast';

import { DesignLandToastRoutingModule } from './toast-routing-module';
import { DesignLandToastComponent } from './toast.component';
import { DesignLandArticleEncapsulatedModule } from '../core/article-encapsulated/article-encapsulated.module';
import { DesignLandExampleViewerModule } from '../core/code-preview/container/example-viewer.module';

@NgModule({
declarations: [
DesignLandToastComponent,
],
imports: [
CommonModule,
RouterModule,
DesignLandToastRoutingModule,
DesignLandExampleViewerModule,
DesignLandArticleEncapsulatedModule,

DaffArticleModule,
DaffToastModule,
],
})
export class DesignLandToastModule {}
7 changes: 7 additions & 0 deletions apps/design-land/src/assets/nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@
"id": "sidebar",
"items": [],
"data": {}
},
{
"title": "Toast",
"url": "toast",
"id": "toast",
"items": [],
"data": {}
}
],
"data": {}
Expand Down
3 changes: 1 addition & 2 deletions libs/design/modal/src/service/modal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ export class DaffModalService {
return this.overlay.create({
hasBackdrop: true,
positionStrategy: new GlobalPositionStrategy()
.centerHorizontally()
.centerVertically(),
.centerHorizontally(),
scrollStrategy: this.overlay.scrollStrategies.block(),
});
}
Expand Down
2 changes: 2 additions & 0 deletions libs/design/scss/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
@use '../sidebar/src/sidebar-theme' as sidebar;
@use '../scss/state/skeleton/mixins' as skeleton;
@use '../tree/src/tree-theme' as tree;
@use '../toast/src/toast-theme' as toast;

//
// Generates the styles of a @daffodil/design theme.
Expand Down Expand Up @@ -81,4 +82,5 @@
@include paginator.daff-paginator-theme($theme);
@include sidebar.daff-sidebar-theme($theme);
@include tree.daff-tree-theme($theme);
@include toast.daff-toast-theme($theme);
}
Loading

0 comments on commit 3d960ef

Please sign in to comment.