diff --git a/libs/components/src/badge-list/badge-list.component.html b/libs/components/src/badge-list/badge-list.component.html
index 18365cba268..d976b2d2cc4 100644
--- a/libs/components/src/badge-list/badge-list.component.html
+++ b/libs/components/src/badge-list/badge-list.component.html
@@ -1,15 +1,15 @@
- @for (item of filteredItems; track item; let last = $last) {
+ @for (item of filteredItems(); track item; let last = $last) {
{{ item }}
- @if (!last || isFiltered) {
+ @if (!last || isFiltered()) {
,
}
}
- @if (isFiltered) {
+ @if (isFiltered()) {
- {{ "plusNMore" | i18n: (items().length - filteredItems.length).toString() }}
+ {{ "plusNMore" | i18n: (items().length - filteredItems().length).toString() }}
}
diff --git a/libs/components/src/badge-list/badge-list.component.ts b/libs/components/src/badge-list/badge-list.component.ts
index e3d1403be43..a5b306c12fc 100644
--- a/libs/components/src/badge-list/badge-list.component.ts
+++ b/libs/components/src/badge-list/badge-list.component.ts
@@ -1,38 +1,60 @@
-import { Component, OnChanges, input } from "@angular/core";
+import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BadgeModule, BadgeVariant } from "../badge";
function transformMaxItems(value: number | undefined) {
- return value == undefined ? undefined : Math.max(1, value);
+ return value == null ? undefined : Math.max(1, value);
}
-// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
-// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
+/**
+ * Displays a collection of badges in a horizontal, wrapping layout.
+ *
+ * The component automatically handles overflow by showing a limited number of badges
+ * followed by a "+N more" badge when `maxItems` is specified and exceeded.
+ *
+ * Each badge inherits the `variant` and `truncate` settings, ensuring visual consistency
+ * across the list. Badges are separated by commas for screen readers to improve accessibility.
+ */
@Component({
selector: "bit-badge-list",
templateUrl: "badge-list.component.html",
imports: [BadgeModule, I18nPipe],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class BadgeListComponent implements OnChanges {
- protected filteredItems: string[] = [];
- protected isFiltered = false;
-
+export class BadgeListComponent {
+ /**
+ * The visual variant to apply to all badges in the list.
+ */
readonly variant = input("primary");
+
+ /**
+ * Items to display as badges.
+ */
readonly items = input([]);
+
+ /**
+ * Whether to truncate long badge text with ellipsis.
+ */
readonly truncate = input(true);
+ /**
+ * Maximum number of badges to display before showing a "+N more" badge.
+ */
readonly maxItems = input(undefined, { transform: transformMaxItems });
- ngOnChanges() {
+ protected readonly filteredItems = computed(() => {
const maxItems = this.maxItems();
+ const items = this.items();
- if (maxItems == undefined || this.items().length <= maxItems) {
- this.filteredItems = this.items();
- } else {
- this.filteredItems = this.items().slice(0, maxItems - 1);
+ if (maxItems == null || items.length <= maxItems) {
+ return items;
}
- this.isFiltered = this.items().length > this.filteredItems.length;
- }
+ return items.slice(0, maxItems - 1);
+ });
+
+ protected readonly isFiltered = computed(() => {
+ return this.items().length > this.filteredItems().length;
+ });
}
diff --git a/libs/components/src/badge/badge.component.ts b/libs/components/src/badge/badge.component.ts
index 8a953b30226..55d7b719ccd 100644
--- a/libs/components/src/badge/badge.component.ts
+++ b/libs/components/src/badge/badge.component.ts
@@ -1,5 +1,12 @@
import { CommonModule } from "@angular/common";
-import { Component, ElementRef, HostBinding, input } from "@angular/core";
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ ElementRef,
+ inject,
+ input,
+} from "@angular/core";
import { FocusableElement } from "../shared/focusable-element";
@@ -44,27 +51,56 @@ const hoverStyles: Record = {
],
};
/**
- * Badges are primarily used as labels, counters, and small buttons.
-
- * Typically Badges are only used with text set to `text-xs`. If additional sizes are needed, the component configurations may be reviewed and adjusted.
-
- * The Badge directive can be used on a `` (non clickable events), or an `` or `