Skip to content

Commit

Permalink
[CL-335][CL-336][CL-374] Announce toasts more consistently (#13167)
Browse files Browse the repository at this point in the history
  • Loading branch information
vleague2 authored Feb 13, 2025
1 parent 86ce9d3 commit fabcf04
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 18 deletions.
9 changes: 6 additions & 3 deletions apps/browser/src/popup/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ import { DesktopSyncVerificationDialogComponent } from "./components/desktop-syn
selector: "app-root",
styles: [],
animations: [routerTransition],
template: ` <div [@routerTransition]="getRouteElevation(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>`,
template: `
<div [@routerTransition]="getRouteElevation(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>
<bit-toast-container></bit-toast-container>
`,
})
export class AppComponent implements OnInit, OnDestroy {
private viewCacheService = inject(PopupViewCacheService);
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
</div>
<router-outlet *ngIf="!loading"></router-outlet>
</div>
<bit-toast-container></bit-toast-container>
`,
})
export class AppComponent implements OnInit, OnDestroy {
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@
<ng-container *ngIf="!loading; else loadingState">
<router-outlet></router-outlet>
</ng-container>

<bit-toast-container></bit-toast-container>
1 change: 1 addition & 0 deletions libs/components/src/toast/toast-container.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div role="status" aria-live="polite" aria-atomic="true" toastContainer></div>
19 changes: 19 additions & 0 deletions libs/components/src/toast/toast-container.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { ToastContainerDirective, ToastrService } from "ngx-toastr";

@Component({
selector: "bit-toast-container",
templateUrl: "toast-container.component.html",
standalone: true,
imports: [ToastContainerDirective],
})
export class ToastContainerComponent implements OnInit {
@ViewChild(ToastContainerDirective, { static: true })
toastContainer?: ToastContainerDirective;

constructor(private toastrService: ToastrService) {}

ngOnInit(): void {
this.toastrService.overlayContainer = this.toastContainer;
}
}
1 change: 1 addition & 0 deletions libs/components/src/toast/toast.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
class="tw-mb-1 tw-min-w-[--bit-toast-width] tw-text-main tw-flex tw-flex-col tw-justify-between tw-rounded-md tw-pointer-events-auto tw-cursor-default tw-overflow-hidden tw-shadow-lg {{
bgColor
}}"
[attr.role]="variant === 'error' ? 'alert' : null"
>
<div class="tw-flex tw-items-center tw-gap-4 tw-px-2 tw-pb-1 tw-pt-2">
<i aria-hidden="true" class="bwi tw-text-xl tw-py-1.5 tw-px-2.5 {{ iconClass }}"></i>
Expand Down
42 changes: 33 additions & 9 deletions libs/components/src/toast/toast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,45 @@ import { ToastService } from "@bitwarden/components";

# Toast

<Primary />
<Controls />
Toasts are ephemeral notifications. They most often communicate the result of a user action. Due to
their ephemeral nature, long messages and critical alerts should not utilize toasts.

## Stories
<Canvas of={stories.Default} />

### Default
<Canvas of={stories.LongContent} />

<Canvas of={stories.Default} />
## Displaying a toast

### Long Content
Toasts are triggered via the `ToastService`, which must be called from a frontend Angular context.

Avoid using long messages in toasts.
```
toastService.showToast({
variant: 'success',
message: 'Hi I'm a toast,
});
```

<Canvas of={stories.LongContent} />
The following options are accepted:

### Service
| Option | Description |
| --------- | --------------------------------------------- |
| `variant` | `"success" \| "error" \| "info" \| "warning"` |
| `title` | Optional title `string` |
| `message` | Main toast content. Required `string` |

<Canvas of={stories.Service} />

## Toast container

`bit-toast-container` should be added to the app root of consuming clients to ensure toasts are
properly announced to screenreaders.

```
<other app file html here>
<bit-toast-container></bit-toast-container>
```

## Accessibility

In addition to the accessibility provided by the `bit-toast-container` component, the toast itself
will apply `aria-alert="true"` if the toast is of type `error`.
5 changes: 3 additions & 2 deletions libs/components/src/toast/toast.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ModuleWithProviders, NgModule } from "@angular/core";
import { DefaultNoComponentGlobalConfig, GlobalConfig, TOAST_CONFIG } from "ngx-toastr";

import { ToastContainerComponent } from "./toast-container.component";
import { BitwardenToastrComponent } from "./toastr.component";

@NgModule({
imports: [BitwardenToastrComponent],
exports: [BitwardenToastrComponent],
imports: [BitwardenToastrComponent, ToastContainerComponent],
exports: [BitwardenToastrComponent, ToastContainerComponent],
})
export class ToastModule {
static forRoot(config: Partial<GlobalConfig> = {}): ModuleWithProviders<ToastModule> {
Expand Down
7 changes: 5 additions & 2 deletions libs/components/src/toast/toast.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default {

decorators: [
moduleMetadata({
imports: [CommonModule, BrowserAnimationsModule, ButtonModule],
imports: [CommonModule, BrowserAnimationsModule, ButtonModule, ToastModule],
declarations: [ToastServiceExampleComponent],
}),
applicationConfig({
Expand All @@ -47,6 +47,7 @@ export default {
success: "Success",
error: "Error",
warning: "Warning",
info: "Info",
});
},
},
Expand Down Expand Up @@ -103,7 +104,9 @@ export const Service: Story = {
props: {
toastOptions: args,
},
template: `
template: /*html*/ `
<!-- Toast container is used here to more closely align with how toasts are used in the clients, which allows for more accurate SR testing in storybook -->
<bit-toast-container></bit-toast-container>
<toast-service-example [toastOptions]="toastOptions"></toast-service-example>
`,
}),
Expand Down
8 changes: 6 additions & 2 deletions libs/components/src/toast/toastr.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { animate, state, style, transition, trigger } from "@angular/animations";
import { Component } from "@angular/core";
import { Toast as BaseToastrComponent } from "ngx-toastr";
import { Toast as BaseToastrComponent, ToastPackage, ToastrService } from "ngx-toastr";

import { ToastComponent } from "./toast.component";

Expand All @@ -27,4 +27,8 @@ import { ToastComponent } from "./toast.component";
standalone: true,
imports: [ToastComponent],
})
export class BitwardenToastrComponent extends BaseToastrComponent {}
export class BitwardenToastrComponent extends BaseToastrComponent {
constructor(toastrService: ToastrService, toastPackage: ToastPackage) {
super(toastrService, toastPackage);
}
}

0 comments on commit fabcf04

Please sign in to comment.