1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Allow custom icons in toasts

This commit is contained in:
Daniel James Smith
2025-03-25 18:54:18 +01:00
parent d0c91db3b3
commit 5c5319a97e
5 changed files with 50 additions and 7 deletions

View File

@@ -5,7 +5,11 @@
[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>
@if (isIcon(icon)) {
<bit-icon class="tw-text-xl tw-py-1.5 tw-px-2.5" [icon]="icon"></bit-icon>
} @else {
<i aria-hidden="true" class="bwi tw-text-xl tw-py-1.5 tw-px-2.5 {{ iconClass }}"></i>
}
<div>
<span class="tw-sr-only">{{ variant | i18n }}</span>
@if (title) {

View File

@@ -1,5 +1,7 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Icon, isIcon } from "../icon";
import { IconModule } from "../icon/icon.module";
import { IconButtonModule } from "../icon-button";
import { SharedModule } from "../shared";
import { TypographyModule } from "../typography";
@@ -29,9 +31,10 @@ const variants: Record<ToastVariant, { icon: string; bgColor: string }> = {
selector: "bit-toast",
templateUrl: "toast.component.html",
standalone: true,
imports: [SharedModule, IconButtonModule, TypographyModule],
imports: [SharedModule, IconButtonModule, TypographyModule, IconModule],
})
export class ToastComponent {
/** The variant of the toast */
@Input() variant: ToastVariant = "info";
/**
@@ -50,10 +53,25 @@ export class ToastComponent {
**/
@Input() progressWidth = 0;
/** An optional icon that overrides the existing variant definition
* string if you want to a use a font icon, or an Icon object if you want to use an SVG icon.
*/
@Input() icon?: string | Icon;
/** Emits when the user presses the close button */
@Output() onClose = new EventEmitter<void>();
/**
* Checks if the provided icon is type of Icon and when that is true returns an Icon
*/
protected isIcon(icon: unknown): icon is Icon {
return isIcon(icon);
}
protected get iconClass(): string {
if (typeof this.icon === "string" && this.icon !== "") {
return this.icon;
}
return variants[this.variant].icon;
}

View File

@@ -11,7 +11,7 @@ export type ToastOptions = {
* The duration the toast will persist in milliseconds
**/
timeout?: number;
} & Pick<ToastComponent, "message" | "variant" | "title">;
} & Pick<ToastComponent, "message" | "variant" | "title" | "icon">;
/**
* Presents toast notifications
@@ -26,6 +26,7 @@ export class ToastService {
message: options.message,
variant: options.variant,
title: options.title,
icon: options?.icon,
},
timeOut:
options.timeout != null && options.timeout > 0

View File

@@ -7,6 +7,7 @@ import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/an
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ButtonModule } from "../button";
import { Icons } from "../icon";
import { I18nMockService } from "../utils/i18n-mock.service";
import { ToastComponent } from "./toast.component";
@@ -76,10 +77,10 @@ export const Default: Story = {
props: args,
template: `
<div class="tw-flex tw-flex-col tw-min-w tw-max-w-[--bit-toast-width]">
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" (onClose)="onClose()" variant="success"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" (onClose)="onClose()" variant="info"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" (onClose)="onClose()" variant="warning"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" (onClose)="onClose()" variant="error"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" [icon]="icon" (onClose)="onClose()" variant="success"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" [icon]="icon" (onClose)="onClose()" variant="info"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" [icon]="icon" (onClose)="onClose()" variant="warning"></bit-toast>
<bit-toast [title]="title" [message]="message" [progressWidth]="progressWidth" [icon]="icon" (onClose)="onClose()" variant="error"></bit-toast>
</div>
`,
}),
@@ -99,6 +100,24 @@ export const LongContent: Story = {
},
};
export const WithCustomIconFromFont: Story = {
...Default,
args: {
title: "Foo",
message: ["With custom icon from font"],
icon: "bwi-send-f",
},
};
export const WithCustomIconUsingSvg: Story = {
...Default,
args: {
title: "Foo",
message: ["With custom svg icon"],
icon: Icons.Search,
},
};
export const Service: Story = {
render: (args) => ({
props: {

View File

@@ -10,6 +10,7 @@ import { ToastComponent } from "./toast.component";
[title]="options?.payload?.title"
[variant]="options?.payload?.variant"
[message]="options?.payload?.message"
[icon]="options?.payload?.icon"
[progressWidth]="width()"
(onClose)="remove()"
></bit-toast>