From 7997d5b7f73bdbd894ade759bcf6e0e7c7cade04 Mon Sep 17 00:00:00 2001 From: xelaint Date: Mon, 10 Mar 2025 19:22:59 -0400 Subject: [PATCH 1/2] docs(design): update toast docs --- libs/design/toast/README.md | 135 ++++++++++-------- .../toast-status/toast-status.component.ts | 7 +- .../toast/src/interfaces/toast-action.ts | 2 +- libs/design/toast/src/public_api.ts | 7 +- .../toast/src/service/position.service.ts | 8 +- .../design/toast/src/service/toast.service.ts | 12 ++ .../toast-actions/toast-actions.directive.ts | 5 +- .../toast-message/toast-message.directive.ts | 5 +- .../src/toast-title/toast-title.directive.ts | 5 +- libs/design/toast/src/toast.module.ts | 43 ------ libs/design/toast/src/toast/toast-config.ts | 26 +++- libs/design/toast/src/toast/toast-provider.ts | 16 +++ .../src/toast/toast-template.component.ts | 135 +++++++++--------- .../design/toast/src/toast/toast.component.ts | 21 ++- 14 files changed, 239 insertions(+), 188 deletions(-) delete mode 100644 libs/design/toast/src/toast.module.ts diff --git a/libs/design/toast/README.md b/libs/design/toast/README.md index 3a9f57320c..fccdd1f894 100644 --- a/libs/design/toast/README.md +++ b/libs/design/toast/README.md @@ -4,19 +4,19 @@ Toasts are small messages designed to mimic push notifications. They are used to ## Overview 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 [Notification](/libs/design/notification/README.md) component. -## Basic Toast +## Basic toast ## Setting up the component -`provideDaffToast()` should be added as a provider either in your application's root component for global use or in a specific feature component. +`provideDaffToast()` must be added as a provider in your application's root component for global use or in a specific feature component for the toast functionality to work properly. ```ts import { provideDaffToast } from '@daffodil/design/toast'; @NgModule({ - providers: [ - provideDaffToast(), - ] + providers: [ + provideDaffToast(), + ] )} export class AppModule {} @@ -34,7 +34,7 @@ import { } from '@angular/core'; import { - DaffToast + DaffToast DaffToastService, } from '@daffodil/design/toast'; @@ -42,22 +42,21 @@ import { selector: 'custom-toast', templateUrl: './custom-toast.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, }) export class CustomToastComponent { - private toast: DaffToast; - - constructor(private toastService: DaffToastService) {} - - open() { - this.toast = this.toastService.open({ - title: 'Update Complete', - message: 'This page has been updated to the newest version.', - }, - { - duration: 5000, - }); - } + private toast: DaffToast; + + constructor(private toastService: DaffToastService) {} + + open() { + this.toast = this.toastService.open({ + title: 'Update Complete', + message: 'This page has been updated to the newest version.', + }, + { + duration: 5000, + }); + } } ``` @@ -67,8 +66,8 @@ The following is an example of a toast with actions: import { ChangeDetectionStrategy, Component, - EventEmitter, - OnInit, + EventEmitter, + OnInit, } from '@angular/core'; import { DAFF_BUTTON_COMPONENTS } from '@daffodil/design/button'; @@ -82,32 +81,31 @@ import { selector: 'action-toast', templateUrl: './action-toast.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - DAFF_BUTTON_COMPONENTS, - ], + imports: [ + DAFF_BUTTON_COMPONENTS, + ], }) export class ActionToastComponent implements OnInit { - private toast: DaffToast; + private toast: DaffToast; - constructor(private toastService: DaffToastService) {} + constructor(private toastService: DaffToastService) {} - update = new EventEmitter(); + update = new EventEmitter(); closeToast = new EventEmitter(); - open() { - this.toast = this.toastService.open({ - title: 'Update Available', - message: 'A new version of this page is available.', - actions: [ - { content: 'Update', color: 'theme-contrast', size: 'sm', eventEmitter: this.update }, - { content: 'Remind me later', type: 'flat', size: 'sm', eventEmitter: this.closeToast }, - ] - }); - } - - ngOnInit() { + open() { + this.toast = this.toastService.open({ + title: 'Update Available', + message: 'A new version of this page is available.', + actions: [ + { content: 'Update', color: 'theme-contrast', size: 'sm', eventEmitter: this.update }, + { content: 'Remind me later', type: 'flat', size: 'sm', eventEmitter: this.closeToast }, + ], + }); + } + + ngOnInit() { this.update.subscribe(() => { }); @@ -124,9 +122,10 @@ The following configurations are available in the `DaffToastService`: | -------- | ------ | ------------------------------- | ------- | | title | string | A quick overview of the toast | -- | | message | string | Additional details about the message that should be limited to one or two sentences | -- | +| statuses | `DaffStatus` | Sets a status on the toast. | - | | actions | `DaffToastAction` | Adds a `daff-button` that allow users to perform an action related to the message. Actions should be limited to two buttons. | -- | | dismissible | boolean | Allows a toast to be dismissible via a close button | true | -| duration | number | The duration in milliseconds that a toast is visible before it's dismissed | 5000 | +| duration | number | The duration in milliseconds that a toast without actions is visible before it's dismissed | 5000ms | The `actions` configurations are based on the properties of the `DaffButtonComponent` (view [Button Documentation](/libs/design/button/README.md)) with the addition of `data` and `eventEmitter`. @@ -144,14 +143,14 @@ By default, a toast without actions will be dismissed after `5000ms`. This can b ### Close button The close button is shown by default but can be hidden by setting `dismissible: false` through the `DaffToastService`. -### Escape Key +### Escape key A toast can be dismissed by using the `ESC` key if it has actions and is focus trapped. ## Stacking A maximum of three toasts can be shown at a time. Toasts are stacked vertically, with the most recent toast displayed on top. ## Statuses -The status color of a toast can be updated by using the `status` property. +Set the `status` property through the `DaffToastService`. Supported statuses: `warn | critical | success` @@ -162,29 +161,47 @@ Supported statuses: `warn | critical | success` | Property | Value | Default | | ------------ | ------------------------ | ------- | -| `vertical` | `top | bottom` | top | -| `horizontal` | `left | center | right ` | right | +| `vertical` | `top` `bottom` | top | +| `horizontal` | `left` `center` `right` | right | -To change the horizontal and vertical position of a toast, add the `provideDaffToastOptions` dependency key to the `providers` key in the module as shown below: +To change the horizontal and vertical position of a toast, add the `provideDaffToastOptions` dependency key to the `providers` key in the component as shown below: ```ts -providers: [ - provideDaffToastOptions({ - position: { - vertical: 'bottom', - horizontal: 'center', - } - useParent: false, - }) -] +@Component({ + selector: 'custom-toast', + templateUrl: './custom-toast.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + provideDaffToastOptions({ + position: { + vertical: 'bottom', + horizontal: 'center', + }, + useParent: false, + }), + ], +}) +export class CustomToastComponent { + private toast: DaffToast; + + constructor(private toastService: DaffToastService) {} + + open() { + this.toast = this.toastService.open({ + title: 'Update Available', + message: 'A new version of this page is available.', + }); + } +} ``` -The position of a toast on a mobile device will always be on the bottom center. +> The position of a toast on a mobile device will always be on the bottom center. -### Toast with configurable positions ## Accessibility -By default, toasts use a `role="status"` to announce messages. It's the equivalent of `aria-live="polite"`, 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 `role="alertdialog"` is used. The toast will be focus trapped and focus immediately moves to the actions. +By default, toasts use a `role="status"` to announce messages. It's the equivalent of `aria-live="polite"`, which does not interrupt a user's current activity and waits until they are idle to make the announcement. + +`role="alertdialog"` is used when a toast has action. The toast will be focus trapped and focus immediately moves to the actions. Avoid setting a duration on toasts with actions because they will disappear automatically, making it difficult for users to interact with the actions. \ No newline at end of file diff --git a/libs/design/toast/examples/src/toast-status/toast-status.component.ts b/libs/design/toast/examples/src/toast-status/toast-status.component.ts index f618765c5f..1de4f50581 100644 --- a/libs/design/toast/examples/src/toast-status/toast-status.component.ts +++ b/libs/design/toast/examples/src/toast-status/toast-status.component.ts @@ -57,9 +57,10 @@ export class ToastStatusComponent { this.toastService.open({ status: this.statusControl.value, ...status[this.statusControl.value], - }, - { - duration: this.statusControl.value === 'critical' ? undefined : 5000, + actions: [ + { content: 'Update', color: 'theme-contrast', size: 'sm' }, + { content: 'Remind me later', type: 'flat', size: 'sm' }, + ], }, ); } diff --git a/libs/design/toast/src/interfaces/toast-action.ts b/libs/design/toast/src/interfaces/toast-action.ts index d48580b4aa..14856cb648 100644 --- a/libs/design/toast/src/interfaces/toast-action.ts +++ b/libs/design/toast/src/interfaces/toast-action.ts @@ -15,7 +15,7 @@ export interface DaffToastAction { type?: 'raised' | 'underline' | 'stroked' | 'flat' | undefined; /** - * The text for the button + * The text for the button. */ content: string; diff --git a/libs/design/toast/src/public_api.ts b/libs/design/toast/src/public_api.ts index 70fa56f105..684bcf0f94 100644 --- a/libs/design/toast/src/public_api.ts +++ b/libs/design/toast/src/public_api.ts @@ -1,5 +1,4 @@ export { DaffToastPositionService } from './service/position.service'; -export { DaffToastModule } from './toast.module'; export { DaffToastService } from './service/toast.service'; export { DaffToastConfiguration } from './toast/toast-config'; export { @@ -14,8 +13,4 @@ export { provideDaffToastOptions, } from './options/daff-toast-options'; -export * from './toast/toast.component'; -export * from './toast-actions/toast-actions.directive'; -export * from './toast-title/toast-title.directive'; -export * from './toast-message/toast-message.directive'; -export * from './toast/toast-provider'; +export { provideDaffToast } from './toast/toast-provider'; diff --git a/libs/design/toast/src/service/position.service.ts b/libs/design/toast/src/service/position.service.ts index c46c07c1eb..2bc2bd812d 100644 --- a/libs/design/toast/src/service/position.service.ts +++ b/libs/design/toast/src/service/position.service.ts @@ -22,16 +22,22 @@ export class DaffToastPositionService { private _config: DaffToastPosition; private _position: DaffToastPosition; + /** + * Reads the current position of the toast. + */ get config(): DaffToastPosition { return this.mediaQuery.isMatched(DaffBreakpoints.MOBILE) ? this._position ?? this._config : { vertical: 'bottom', horizontal: 'center' }; } - set config(val: DaffToastPosition) { + private set config(val: DaffToastPosition) { this._config = val; } + /** + * Changes the position of the toast. + */ setPosition(position: DaffToastPosition) { this._position = position; } diff --git a/libs/design/toast/src/service/toast.service.ts b/libs/design/toast/src/service/toast.service.ts index 45003bc8e4..f55a4947ed 100644 --- a/libs/design/toast/src/service/toast.service.ts +++ b/libs/design/toast/src/service/toast.service.ts @@ -50,6 +50,9 @@ import { } from '../toast/toast-config'; import { DaffToastTemplateComponent } from '../toast/toast-template.component'; +/** + * Service to display toasts. + */ @Injectable() export class DaffToastService implements OnDestroy { @@ -77,6 +80,9 @@ export class DaffToastService implements OnDestroy { ).subscribe(); } + /** + * @docs-private + */ ngOnDestroy(): void { this._sub.unsubscribe(); } @@ -96,6 +102,9 @@ export class DaffToastService implements OnDestroy { }); } + /** + * Opens the toast. See DaffToastConfiguration for configuration options. + */ open( toast: DaffToastData, configuration?: Partial, @@ -143,6 +152,9 @@ export class DaffToastService implements OnDestroy { return _toastPlus; } + /** + * Closes the toast. + */ close(toast: DaffToast): void { if(this._parentToast && this.options.useParent) { this._parentToast.close(toast); diff --git a/libs/design/toast/src/toast-actions/toast-actions.directive.ts b/libs/design/toast/src/toast-actions/toast-actions.directive.ts index 8faf3bc4c2..919bf38266 100644 --- a/libs/design/toast/src/toast-actions/toast-actions.directive.ts +++ b/libs/design/toast/src/toast-actions/toast-actions.directive.ts @@ -5,10 +5,11 @@ import { @Directive({ selector: '[daffToastActions]', - standalone: true, }) export class DaffToastActionsDirective { - + /** + * @docs-private + */ @HostBinding('class.daff-toast__actions') class = true; } diff --git a/libs/design/toast/src/toast-message/toast-message.directive.ts b/libs/design/toast/src/toast-message/toast-message.directive.ts index 4ec3b43af7..8ffcec06b3 100644 --- a/libs/design/toast/src/toast-message/toast-message.directive.ts +++ b/libs/design/toast/src/toast-message/toast-message.directive.ts @@ -5,10 +5,11 @@ import { @Directive({ selector: '[daffToastMessage]', - standalone: true, }) export class DaffToastMessageDirective { - + /** + * @docs-private + */ @HostBinding('class.daff-toast__message') class = true; } diff --git a/libs/design/toast/src/toast-title/toast-title.directive.ts b/libs/design/toast/src/toast-title/toast-title.directive.ts index f002c2cfbb..f81e9613f7 100644 --- a/libs/design/toast/src/toast-title/toast-title.directive.ts +++ b/libs/design/toast/src/toast-title/toast-title.directive.ts @@ -5,10 +5,11 @@ import { @Directive({ selector: '[daffToastTitle]', - standalone: true, }) export class DaffToastTitleDirective { - + /** + * @docs-private + */ @HostBinding('class.daff-toast__title') class = true; } diff --git a/libs/design/toast/src/toast.module.ts b/libs/design/toast/src/toast.module.ts deleted file mode 100644 index cc258eb2e9..0000000000 --- a/libs/design/toast/src/toast.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { OverlayModule } from '@angular/cdk/overlay'; -import { PortalModule } from '@angular/cdk/portal'; -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; - -import { DaffPrefixSuffixModule } from '@daffodil/design'; -import { DaffButtonModule } from '@daffodil/design/button'; - -import { DaffToastPositionService } from './service/position.service'; -import { DaffToastComponent } from './toast/toast.component'; -import { DaffToastActionsDirective } from './toast-actions/toast-actions.directive'; -import { DaffToastMessageDirective } from './toast-message/toast-message.directive'; -import { DaffToastTitleDirective } from './toast-title/toast-title.directive'; - -/** - * @deprecated in favor of {@link provideDaffToast}. Deprecated in version 0.78.0. Will be removed in version 1.0.0. - */ -@NgModule({ - imports: [ - CommonModule, - DaffPrefixSuffixModule, - DaffButtonModule, - FontAwesomeModule, - PortalModule, - OverlayModule, - DaffToastComponent, - DaffToastActionsDirective, - DaffToastTitleDirective, - DaffToastMessageDirective, - ], - exports: [ - DaffToastComponent, - DaffToastActionsDirective, - DaffToastTitleDirective, - DaffToastMessageDirective, - DaffPrefixSuffixModule, - ], - providers: [ - DaffToastPositionService, - ], -}) -export class DaffToastModule { } diff --git a/libs/design/toast/src/toast/toast-config.ts b/libs/design/toast/src/toast/toast-config.ts index 556d097c59..8438c27ff9 100644 --- a/libs/design/toast/src/toast/toast-config.ts +++ b/libs/design/toast/src/toast/toast-config.ts @@ -1,6 +1,30 @@ export interface DaffToastConfiguration { /** - * The duration (in milliseconds) that a toast is visible before it's dismissed. + * The duration (in milliseconds) for which a toast remains visible before dismissal. + * By default, toasts without actions are displayed for 5000ms. + * + * While you can set a duration for toasts with actions, it's generally not recommended, + * as users should have ample time to interact with them. + * + * @usage + * ``` + * export class CustomComponent { + * private toast: DaffToast; + * + * constructor(private toastService: DaffToastService) {} + * + * open() { + * this.toast = this.toastService.open({ + * title: 'Update Complete', + * message: 'This page has been updated to the newest version.', + * status: 'success', + * }, + * { + * duration: 7000, + * }); + * } + * } + * ``` */ duration?: number; } diff --git a/libs/design/toast/src/toast/toast-provider.ts b/libs/design/toast/src/toast/toast-provider.ts index db6e48dfd7..d8a8c38bc5 100644 --- a/libs/design/toast/src/toast/toast-provider.ts +++ b/libs/design/toast/src/toast/toast-provider.ts @@ -3,6 +3,22 @@ import { Provider } from '@angular/core'; import { DaffToastPositionService } from '../service/position.service'; import { DaffToastService } from '../service/toast.service'; +/** + * Registers the `DaffToastService` and `DaffToastPositionService` for displaying a toast. This provider ensures + * toasts function correctly within your application. + * + * ```ts + * import { provideDaffToast } from '@daffodil/design/toast'; + * + * @NgModule({ + * providers: [ + * provideDaffToast(), + * ] + * )} + * + * export class AppModule {} + * ``` + */ export const provideDaffToast = (): Provider[] => [ DaffToastService, DaffToastPositionService, diff --git a/libs/design/toast/src/toast/toast-template.component.ts b/libs/design/toast/src/toast/toast-template.component.ts index 0a78e28d97..5a1aec142b 100644 --- a/libs/design/toast/src/toast/toast-template.component.ts +++ b/libs/design/toast/src/toast/toast-template.component.ts @@ -6,10 +6,6 @@ import { } from '@angular/animations'; import { NgFor, - NgIf, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, NgTemplateOutlet, SlicePipe, } from '@angular/common'; @@ -29,10 +25,7 @@ import { DAFF_BUTTON_COMPONENTS } from '@daffodil/design/button'; import { DaffToastComponent } from './toast.component'; import { DaffToast } from '../interfaces/toast'; -import { - DaffToastOptions, - DAFF_TOAST_OPTIONS, -} from '../options/daff-toast-options'; +import { DAFF_TOAST_OPTIONS } from '../options/daff-toast-options'; import { DaffToastPositionService } from '../service/position.service'; import { DaffToastActionsDirective } from '../toast-actions/toast-actions.directive'; import { DaffToastMessageDirective } from '../toast-message/toast-message.directive'; @@ -52,63 +45,76 @@ import { DaffToastTitleDirective } from '../toast-title/toast-title.directive'; [attr.aria-describedby]="item.actions ? item.message : undefined">
{{ item.title }}
{{ item.message }}
-
- - - -
- + @if (item.actions) { +
+ @for (action of item.actions; track action) { + + } +
+ } + @if (item.dismissible) { + + } - - - - - - - + @switch (action.type) { + @case ('stroked') { + + } + @case ('raised') { + + } + @case ('flat') { + + } + @case ('underline') { + + } + @default { + + } + } `, animations: [ @@ -131,11 +137,7 @@ import { DaffToastTitleDirective } from '../toast-title/toast-title.directive'; DaffToastTitleDirective, DaffToastMessageDirective, FaIconComponent, - NgSwitch, NgFor, - NgSwitchCase, - NgSwitchDefault, - NgIf, SlicePipe, NgTemplateOutlet, ], @@ -148,7 +150,6 @@ export class DaffToastTemplateComponent { constructor( private cd: ChangeDetectorRef, @Inject(DAFF_TOAST_OPTIONS) - private options: DaffToastOptions, private toastPosition: DaffToastPositionService, ) { } diff --git a/libs/design/toast/src/toast/toast.component.ts b/libs/design/toast/src/toast/toast.component.ts index 46bc0c9e5c..e84d68c935 100644 --- a/libs/design/toast/src/toast/toast.component.ts +++ b/libs/design/toast/src/toast/toast.component.ts @@ -30,6 +30,8 @@ import { daffToastChangesFocus } from '../service/changes-focus'; import { DaffToastActionsDirective } from '../toast-actions/toast-actions.directive'; /** + * @docs-private + * * DaffToastComponent provides a way to display and * communicate information for user actions or system updates. */ @@ -51,12 +53,20 @@ import { DaffToastActionsDirective } from '../toast-actions/toast-actions.direct ], }) export class DaffToastComponent implements DaffPrefixable, AfterContentInit, AfterViewInit, OnDestroy { - /** @docs-private */ + /** + * @docs-private + */ @HostBinding('class.daff-toast') class = true; + /** + * @docs-private + */ @ContentChild(DaffToastActionsDirective) _actions: DaffToastActionsDirective; + /** + * @docs-private + */ @ContentChild(DaffPrefixDirective) _prefix: DaffPrefixDirective; @@ -79,6 +89,9 @@ export class DaffToastComponent implements DaffPrefixable, AfterContentInit, Aft ) { } + /** + * @docs-private + */ ngAfterContentInit() { if(daffToastChangesFocus(this.toast)) { this._focusTrap = this._focusTrapFactory.create( @@ -87,6 +100,9 @@ export class DaffToastComponent implements DaffPrefixable, AfterContentInit, Aft } } + /** + * @docs-private + */ ngAfterViewInit() { if(daffToastChangesFocus(this.toast)) { this._focusStack.push(); @@ -94,6 +110,9 @@ export class DaffToastComponent implements DaffPrefixable, AfterContentInit, Aft } } + /** + * @docs-private + */ ngOnDestroy() { if(daffToastChangesFocus(this.toast)) { this._focusTrap.destroy(); From 836d5172a8c1e2077adbcdcce0d88df5e7866a0b Mon Sep 17 00:00:00 2001 From: xelaint Date: Tue, 11 Mar 2025 15:22:51 -0400 Subject: [PATCH 2/2] updates --- libs/design/toast/README.md | 175 ++---------------- .../dismissible-toast.component.html | 1 + .../dismissible-toast.component.ts | 36 ++++ libs/design/toast/examples/src/public_api.ts | 2 + .../toast/src/options/daff-toast-options.ts | 20 +- .../design/toast/src/service/toast.service.ts | 53 +++++- 6 files changed, 118 insertions(+), 169 deletions(-) create mode 100644 libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.html create mode 100644 libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.ts diff --git a/libs/design/toast/README.md b/libs/design/toast/README.md index fccdd1f894..21006cf97f 100644 --- a/libs/design/toast/README.md +++ b/libs/design/toast/README.md @@ -22,186 +22,39 @@ import { provideDaffToast } from '@daffodil/design/toast'; export class AppModule {} ``` -### Configurations -Toast can be configured by using the `DaffToastService`. - -The following is an example of a toast with a duration: - -```ts -import { - ChangeDetectionStrategy, - Component, -} from '@angular/core'; - -import { - DaffToast - DaffToastService, -} from '@daffodil/design/toast'; - -@Component({ - selector: 'custom-toast', - templateUrl: './custom-toast.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CustomToastComponent { - private toast: DaffToast; - - constructor(private toastService: DaffToastService) {} - - open() { - this.toast = this.toastService.open({ - title: 'Update Complete', - message: 'This page has been updated to the newest version.', - }, - { - duration: 5000, - }); - } -} -``` - -The following is an example of a toast with actions: - -```ts -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - OnInit, -} from '@angular/core'; - -import { DAFF_BUTTON_COMPONENTS } from '@daffodil/design/button'; -import { - DaffToast, - DaffToastAction, - DaffToastService, -} from '@daffodil/design/toast'; - -@Component({ - selector: 'action-toast', - templateUrl: './action-toast.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - DAFF_BUTTON_COMPONENTS, - ], -}) -export class ActionToastComponent implements OnInit { - private toast: DaffToast; - - constructor(private toastService: DaffToastService) {} - - update = new EventEmitter(); - - closeToast = new EventEmitter(); - - open() { - this.toast = this.toastService.open({ - title: 'Update Available', - message: 'A new version of this page is available.', - actions: [ - { content: 'Update', color: 'theme-contrast', size: 'sm', eventEmitter: this.update }, - { content: 'Remind me later', type: 'flat', size: 'sm', eventEmitter: this.closeToast }, - ], - }); - } - - ngOnInit() { - this.update.subscribe(() => { - }); - - this.closeToast.subscribe(() => { - this.toastService.close(this.toast); - }); - } -} -``` - -The following configurations are available in the `DaffToastService`: - -| Property | Type | Description | Default | -| -------- | ------ | ------------------------------- | ------- | -| title | string | A quick overview of the toast | -- | -| message | string | Additional details about the message that should be limited to one or two sentences | -- | -| statuses | `DaffStatus` | Sets a status on the toast. | - | -| actions | `DaffToastAction` | Adds a `daff-button` that allow users to perform an action related to the message. Actions should be limited to two buttons. | -- | -| dismissible | boolean | Allows a toast to be dismissible via a close button | true | -| duration | number | The duration in milliseconds that a toast without actions is visible before it's dismissed | 5000ms | - -The `actions` configurations are based on the properties of the `DaffButtonComponent` (view [Button Documentation](/libs/design/button/README.md)) with the addition of `data` and `eventEmitter`. - ## Dismissal -A toast can be dismissed via a timed duration, a close button, or the `ESC` key. +A toast can be dismissed via a timed duration or a close button. + +The `duration` and `dismissible` properties can be updated when you open it with the `DaffToastService`. ### Timed duration -A toast with actions will persist until one of the actions have been interacted with, or is dismissed by the close button or the `ESC` key. Actionable toasts should be persistent, but a duration is allowed to be set. If duration must be set, make sure it's long enough for users to engage with the actions. +A toast with actions will persist until one of the actions have been interacted with or dismissed by the close button. By default, a toast without actions will be dismissed after `5000ms`. -By default, a toast without actions will be dismissed after `5000ms`. This can be updated by setting `duration` through the `DaffToastService`. +> Actionable toasts should be persistent, but a duration is allowed to be set. If duration must be set, make sure it's long enough for users to engage with the actions. -#### Toast with custom duration ### Close button -The close button is shown by default but can be hidden by setting `dismissible: false` through the `DaffToastService`. +The close button is hidden by default, and dismissible is ignored when there are actions in a toast. You can change the visibility of the close button via the `dismissible` property. -### Escape key -A toast can be dismissed by using the `ESC` key if it has actions and is focus trapped. + ## Stacking A maximum of three toasts can be shown at a time. Toasts are stacked vertically, with the most recent toast displayed on top. ## Statuses -Set the `status` property through the `DaffToastService`. - -Supported statuses: `warn | critical | success` +You can set the status of a toast when opening it with `DaffToastService`, using values defined by `DaffStatus`. -### Toast with statuses -## Positions - -| Property | Value | Default | -| ------------ | ------------------------ | ------- | -| `vertical` | `top` `bottom` | top | -| `horizontal` | `left` `center` `right` | right | - -To change the horizontal and vertical position of a toast, add the `provideDaffToastOptions` dependency key to the `providers` key in the component as shown below: - -```ts -@Component({ - selector: 'custom-toast', - templateUrl: './custom-toast.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - provideDaffToastOptions({ - position: { - vertical: 'bottom', - horizontal: 'center', - }, - useParent: false, - }), - ], -}) -export class CustomToastComponent { - private toast: DaffToast; - - constructor(private toastService: DaffToastService) {} - - open() { - this.toast = this.toastService.open({ - title: 'Update Available', - message: 'A new version of this page is available.', - }); - } -} -``` - -> The position of a toast on a mobile device will always be on the bottom center. - - - ## Accessibility By default, toasts use a `role="status"` to announce messages. It's the equivalent of `aria-live="polite"`, which does not interrupt a user's current activity and waits until they are idle to make the announcement. `role="alertdialog"` is used when a toast has action. The toast will be focus trapped and focus immediately moves to the actions. -Avoid setting a duration on toasts with actions because they will disappear automatically, making it difficult for users to interact with the actions. \ No newline at end of file +Avoid setting a duration on toasts with actions because they will disappear automatically, making it difficult for users to interact with the actions. + +### Keyboard Interactions +| Key | Action | +| ----- | ------ | +| `ESC` | Dismisses a toast if it has actions and is focus trapped. | \ No newline at end of file diff --git a/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.html b/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.html new file mode 100644 index 0000000000..d48b22c39e --- /dev/null +++ b/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.ts b/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.ts new file mode 100644 index 0000000000..b402c452f3 --- /dev/null +++ b/libs/design/toast/examples/src/dismissible-toast/dismissible-toast.component.ts @@ -0,0 +1,36 @@ +import { + ChangeDetectionStrategy, + Component, +} from '@angular/core'; + +import { DaffButtonComponent } from '@daffodil/design/button'; +import { + DaffToast, + DaffToastService, +} from '@daffodil/design/toast'; + +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: 'dismissible-toast', + templateUrl: './dismissible-toast.component.html', + styles: [], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DaffButtonComponent, + ], +}) +export class DismissibleToastComponent { + private toast: DaffToast; + + constructor(private toastService: DaffToastService) {} + + open() { + this.toast = this.toastService.open({ + title: 'Update Available' + ' ' + this.count++, + message: 'A new version of this page is available.', + dismissible: true, + }); + } + + private count = 0; +} diff --git a/libs/design/toast/examples/src/public_api.ts b/libs/design/toast/examples/src/public_api.ts index 23a7e25bea..b8a6778b06 100644 --- a/libs/design/toast/examples/src/public_api.ts +++ b/libs/design/toast/examples/src/public_api.ts @@ -1,4 +1,5 @@ import { DefaultToastComponent } from './default-toast/default-toast.component'; +import { DismissibleToastComponent } from './dismissible-toast/dismissible-toast.component'; import { ToastPositionsComponent } from './toast-positions/toast-positions.component'; import { ToastStatusComponent } from './toast-status/toast-status.component'; import { ToastWithCustomDurationComponent } from './toast-with-custom-duration/toast-with-custom-duration.component'; @@ -8,4 +9,5 @@ export const TOAST_EXAMPLES = [ ToastPositionsComponent, ToastStatusComponent, ToastWithCustomDurationComponent, + DismissibleToastComponent, ]; diff --git a/libs/design/toast/src/options/daff-toast-options.ts b/libs/design/toast/src/options/daff-toast-options.ts index 098b3b4e3d..b10e2b48e5 100644 --- a/libs/design/toast/src/options/daff-toast-options.ts +++ b/libs/design/toast/src/options/daff-toast-options.ts @@ -22,10 +22,16 @@ export const daffToastDefaultOptions: DaffToastOptions = { useParent: true, }; -export const { - token: DAFF_TOAST_OPTIONS, - /** - * Provider function for {@link DAFF_TOAST_OPTIONS}. - */ - provider: provideDaffToastOptions, -} = createConfigInjectionToken(daffToastDefaultOptions, 'DAFF_TOAST_OPTIONS'); +const result = createConfigInjectionToken(daffToastDefaultOptions, 'DAFF_TOAST_OPTIONS'); + +export const provideDaffToastOptions = result.provider; + +export const DAFF_TOAST_OPTIONS = result.token; + +// export const { +// token: DAFF_TOAST_OPTIONS, +// /** +// * Provider function for {@link DAFF_TOAST_OPTIONS}. +// */ +// provider: provideDaffToastOptions, +// } = createConfigInjectionToken(daffToastDefaultOptions, 'DAFF_TOAST_OPTIONS'); diff --git a/libs/design/toast/src/service/toast.service.ts b/libs/design/toast/src/service/toast.service.ts index f55a4947ed..6b7245dc46 100644 --- a/libs/design/toast/src/service/toast.service.ts +++ b/libs/design/toast/src/service/toast.service.ts @@ -52,6 +52,52 @@ import { DaffToastTemplateComponent } from '../toast/toast-template.component'; /** * Service to display toasts. + * + * @example + * ```ts + * import { + * ChangeDetectionStrategy, + * Component, + * EventEmitter, + * OnInit, + * } from '@angular/core'; + * + * import { + * DaffToast, + * DaffToastAction, + * DaffToastService, + * } from '@daffodil/design/toast'; + * + * @Component({ + * selector: 'default-toast', + * templateUrl: './default-toast.component.html', + * styles: [], + * changeDetection: ChangeDetectionStrategy.OnPush, + * }) + * export class DefaultToastComponent implements OnInit { + * private toast: DaffToast; + * + * constructor(private toastService: DaffToastService) {} + * + * closeToast = new EventEmitter(); + * + * open() { + * this.toast = this.toastService.open({ + * title: 'Update Available', + * message: 'A new version of this page is available.', + * actions: [ + * { content: 'Remind me later', type: 'flat', size: 'sm', eventEmitter: this.closeToast }, + * ], + * }); + * } + * + * ngOnInit() { + * this.closeToast.subscribe(() => { + * this.toastService.close(this.toast); + * }); + * } + * } + * ``` */ @Injectable() export class DaffToastService implements OnDestroy { @@ -103,7 +149,10 @@ export class DaffToastService implements OnDestroy { } /** - * Opens the toast. See DaffToastConfiguration for configuration options. + * Opens the toast. + * + * @param toast Data that can be shown on a toast. + * @param configuration Additional configuration options such as duration. */ open( toast: DaffToastData, @@ -154,6 +203,8 @@ export class DaffToastService implements OnDestroy { /** * Closes the toast. + * + * @param toast The instance of toast that you wish to close. */ close(toast: DaffToast): void { if(this._parentToast && this.options.useParent) {