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

[CL-335][CL-336][CL-374] Announce toasts more consistently #13167

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
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 @@ -92,6 +92,8 @@
</div>
<router-outlet *ngIf="!loading"></router-outlet>
</div>

<bit-toast-container></bit-toast-container>

Check warning on line 96 in apps/desktop/src/app/app.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/desktop/src/app/app.component.ts#L96

Added line #L96 was not covered by tests
`,
})
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) {}

Check warning on line 14 in libs/components/src/toast/toast-container.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/toast/toast-container.component.ts#L14

Added line #L14 was not covered by tests

ngOnInit(): void {
this.toastrService.overlayContainer = this.toastContainer;

Check warning on line 17 in libs/components/src/toast/toast-container.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/toast/toast-container.component.ts#L17

Added line #L17 was not covered by tests
}
}
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
54 changes: 45 additions & 9 deletions libs/components/src/toast/toast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,57 @@ import { ToastService } from "@bitwarden/components";

# Toast

<Primary />
<Controls />
Toasts are used for error or confirmation messaging. The toast body content should accurately
vleague2 marked this conversation as resolved.
Show resolved Hide resolved
describe the warning, error, or successful event following best practices for UX Writing. Avoid
using long messages in 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` from a frontend Angular context.
vleague2 marked this conversation as resolved.
Show resolved Hide resolved

Avoid using long messages in toasts.
```
toastService.showToast({
variant: 'success',
title: null,
message: 'Hi I'm a toast,
});
vleague2 marked this conversation as resolved.
Show resolved Hide resolved
```

<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

Each web client uses the `bit-toast-container` component in its root `app` component to house the
toasts. This container component provides aria attributes that make the toasts accessible to
screenreader technology. Any new clients should do the same. Without the toast container, toasts
will still display visually but will be more difficult for screenreader users to perceive.
vleague2 marked this conversation as resolved.
Show resolved Hide resolved

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

The `Service` story utilizes the `bit-toast-container` for more accurate screenreader testing.
vleague2 marked this conversation as resolved.
Show resolved Hide resolved

## Toast configuration

Toast placement, auto dismiss, close button visibility, and max number of toasts to display are
configured once at the shared module import level, but can be overridden in a client's module import
file.

vleague2 marked this conversation as resolved.
Show resolved Hide resolved
## 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
4 changes: 3 additions & 1 deletion 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 @@ -104,6 +105,7 @@ export const Service: Story = {
toastOptions: args,
},
template: `
<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 @@
standalone: true,
imports: [ToastComponent],
})
export class BitwardenToastrComponent extends BaseToastrComponent {}
export class BitwardenToastrComponent extends BaseToastrComponent {
constructor(toastrService: ToastrService, toastPackage: ToastPackage) {
super(toastrService, toastPackage);

Check warning on line 32 in libs/components/src/toast/toastr.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/components/src/toast/toastr.component.ts#L32

Added line #L32 was not covered by tests
Comment on lines +31 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โ„น๏ธ storybookjs/storybook#23534

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find

}
}
Loading