From 5c5319a97e215565debc8e32d1480d8520db8dd4 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Tue, 25 Mar 2025 18:54:18 +0100 Subject: [PATCH] Allow custom icons in toasts --- .../components/src/toast/toast.component.html | 6 ++++- libs/components/src/toast/toast.component.ts | 20 +++++++++++++- libs/components/src/toast/toast.service.ts | 3 ++- libs/components/src/toast/toast.stories.ts | 27 ++++++++++++++++--- libs/components/src/toast/toastr.component.ts | 1 + 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/libs/components/src/toast/toast.component.html b/libs/components/src/toast/toast.component.html index bdbc9674184..113e3b2f83e 100644 --- a/libs/components/src/toast/toast.component.html +++ b/libs/components/src/toast/toast.component.html @@ -5,7 +5,11 @@ [attr.role]="variant === 'error' ? 'alert' : null" >
- + @if (isIcon(icon)) { + + } @else { + + }
{{ variant | i18n }} @if (title) { diff --git a/libs/components/src/toast/toast.component.ts b/libs/components/src/toast/toast.component.ts index bbf0291f180..84c51322964 100644 --- a/libs/components/src/toast/toast.component.ts +++ b/libs/components/src/toast/toast.component.ts @@ -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 = { 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(); + /** + * 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; } diff --git a/libs/components/src/toast/toast.service.ts b/libs/components/src/toast/toast.service.ts index 00c32f9b1b3..0af029cfea1 100644 --- a/libs/components/src/toast/toast.service.ts +++ b/libs/components/src/toast/toast.service.ts @@ -11,7 +11,7 @@ export type ToastOptions = { * The duration the toast will persist in milliseconds **/ timeout?: number; -} & Pick; +} & Pick; /** * 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 diff --git a/libs/components/src/toast/toast.stories.ts b/libs/components/src/toast/toast.stories.ts index 0af4974eead..cf0b1015112 100644 --- a/libs/components/src/toast/toast.stories.ts +++ b/libs/components/src/toast/toast.stories.ts @@ -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: `
- - - - + + + +
`, }), @@ -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: { diff --git a/libs/components/src/toast/toastr.component.ts b/libs/components/src/toast/toastr.component.ts index 75124ceb4b3..c348db380b7 100644 --- a/libs/components/src/toast/toastr.component.ts +++ b/libs/components/src/toast/toastr.component.ts @@ -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()" >