Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(design): update toast docs #3556

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 21 additions & 151 deletions libs/design/toast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,187 +4,57 @@ 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
<design-land-example-viewer-container example="default-toast"></design-land-example-viewer-container>

## 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 {}
```

### 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,
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,
});
}
}
```

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,
standalone: true,
imports: [
DAFF_BUTTON_COMPONENTS,
],
})
export class ActionToastComponent implements OnInit {
private toast: DaffToast;

constructor(private toastService: DaffToastService) {}

update = new EventEmitter<DaffToastAction>();

closeToast = new EventEmitter<DaffToastAction>();

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 | -- |
| 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 |

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
<design-land-example-viewer-container example="toast-with-custom-duration"></design-land-example-viewer-container>

### 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.
<design-land-example-viewer-container example="dismissible-toast"></design-land-example-viewer-container>

## 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.

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
<design-land-example-viewer-container example="toast-status"></design-land-example-viewer-container>

## 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 module as shown below:

```ts
providers: [
provideDaffToastOptions({
position: {
vertical: 'bottom',
horizontal: 'center',
}
useParent: false,
})
]
```

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.

### Toast with configurable positions
<design-land-example-viewer-container example="toast-positions"></design-land-example-viewer-container>
`role="alertdialog"` is used when a toast has action. The toast will be focus trapped and focus immediately moves to the actions.

## 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.
Avoid setting a duration on toasts with actions because they will disappear automatically, making it difficult for users to interact with 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.
### Keyboard Interactions
| Key | Action |
| ----- | ------ |
| `ESC` | Dismisses a toast if it has actions and is focus trapped. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<button daff-button size="sm" (click)="open()">Show toast</button>
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions libs/design/toast/examples/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -8,4 +9,5 @@ export const TOAST_EXAMPLES = [
ToastPositionsComponent,
ToastStatusComponent,
ToastWithCustomDurationComponent,
DismissibleToastComponent,
];
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
],
},
);
}
Expand Down
2 changes: 1 addition & 1 deletion libs/design/toast/src/interfaces/toast-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
20 changes: 13 additions & 7 deletions libs/design/toast/src/options/daff-toast-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DaffToastOptions>(daffToastDefaultOptions, 'DAFF_TOAST_OPTIONS');
const result = createConfigInjectionToken<DaffToastOptions>(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<DaffToastOptions>(daffToastDefaultOptions, 'DAFF_TOAST_OPTIONS');
7 changes: 1 addition & 6 deletions libs/design/toast/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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';
8 changes: 7 additions & 1 deletion libs/design/toast/src/service/position.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
: <DaffToastPosition>{ 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;
}
Expand Down
Loading