1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 06:23:38 +00:00

[EC-86] Add bit-badge-list component and tweak BadgeModule to support both the component and directive.

Update mockI18nService to support templated strings.
This commit is contained in:
Shane Melton
2022-10-07 17:35:17 -07:00
parent d253cfef5d
commit a72b152598
9 changed files with 122 additions and 28 deletions

View File

@@ -81,22 +81,11 @@
</button>
</td>
<td bitCell (click)="edit(g)" class="tw-cursor-pointer">
<span
*ngFor="let cName of g.collectionNames.slice(0, maxCollections); let last = last"
bitBadge
badgeType="secondary"
class="tw-mx-1"
>
{{ cName }} <span class="sr-only" *ngIf="!last">, </span>
</span>
<span
*ngIf="g.collectionNames.length > maxCollections"
bitBadge
badgeType="secondary"
class="tw-mx-1"
>
{{ "plusNMore" | i18n: (g.collectionNames.length - maxCollections).toString() }}
</span>
<bit-badge-list
[items]="g.collectionNames"
[maxItems]="2"
badgeType="warning"
></bit-badge-list>
</td>
<td bitCell>
<button

View File

@@ -71,7 +71,6 @@ export class GroupsComponent implements OnInit, OnDestroy {
protected didScroll = false;
protected pageSize = 100;
protected maxCollections = 2;
private pagedGroupsCount = 0;
private pagedGroups: GroupDetailsRow[];

View File

@@ -0,0 +1,12 @@
<span
*ngFor="let item of filteredItems; let last = last"
bitBadge
[badgeType]="badgeType"
class="tw-mx-1"
>
{{ item }}
<span class="sr-only" *ngIf="!last">, </span>
</span>
<span *ngIf="isFiltered" bitBadge [badgeType]="badgeType" class="tw-mx-1">
{{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }}
</span>

View File

@@ -0,0 +1,48 @@
import { Component, Input } from "@angular/core";
import { BadgeTypes } from "./badge.directive";
@Component({
selector: "bit-badge-list",
templateUrl: "badge-list.component.html",
})
export class BadgeListComponent {
private _maxItems: number | null = null;
private _items: string[] = [];
protected filteredItems: string[] = [];
@Input() badgeType: BadgeTypes = "primary";
@Input()
get maxItems(): number | null {
return this._maxItems;
}
set maxItems(value: number | null) {
this._maxItems = value == null ? null : Math.max(1, value);
this.updateFilteredItems();
}
@Input()
get items(): string[] {
return this._items;
}
set items(value: string[]) {
this._items = value;
this.updateFilteredItems();
}
protected get isFiltered(): boolean {
return this._items.length > this.filteredItems.length;
}
private updateFilteredItems() {
if (this.maxItems == null) {
this.filteredItems = this._items;
} else {
this.filteredItems = this._items.slice(0, this.maxItems);
}
}
}

View File

@@ -1,6 +1,6 @@
import { Directive, ElementRef, HostBinding, Input } from "@angular/core";
type BadgeTypes = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
export type BadgeTypes = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
const styles: Record<BadgeTypes, string[]> = {
primary: ["tw-bg-primary-500"],

View File

@@ -1,11 +1,13 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { SharedModule } from "../shared";
import { BadgeListComponent } from "./badge-list.component";
import { BadgeDirective } from "./badge.directive";
@NgModule({
imports: [CommonModule],
exports: [BadgeDirective],
declarations: [BadgeDirective],
imports: [SharedModule],
exports: [BadgeDirective, BadgeListComponent],
declarations: [BadgeDirective, BadgeListComponent],
})
export class BadgeModule {}

View File

@@ -1,13 +1,40 @@
import { Meta, Story } from "@storybook/angular";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { BadgeDirective } from "./badge.directive";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { SharedModule } from "../shared";
import { I18nMockService } from "../utils/i18n-mock.service";
import { BadgeListComponent } from "./badge-list.component";
import { BadgeDirective, BadgeTypes } from "./badge.directive";
export default {
title: "Component Library/Badge",
component: BadgeDirective,
decorators: [
moduleMetadata({
imports: [SharedModule],
declarations: [BadgeDirective, BadgeListComponent],
providers: [
{
provide: I18nService,
useFactory: () => {
return new I18nMockService({
plusNMore: (n) => `+ ${n} more`,
});
},
},
],
}),
],
args: {
badgeType: "primary",
},
argTypes: {
badgeType: {
options: ["primary", "secondary", "success", "danger", "warning", "info"] as BadgeTypes[],
control: { type: "inline-radio" },
},
},
parameters: {
design: {
type: "figma",
@@ -54,3 +81,16 @@ export const Info = Template.bind({});
Info.args = {
badgeType: "info",
};
const ListTemplate: Story<BadgeListComponent> = (args: BadgeListComponent) => ({
props: args,
template: `
<bit-badge-list [badgeType]="badgeType" [maxItems]="maxItems" [items]="items"></bit-badge-list>`,
});
export const BadgeList = ListTemplate.bind({});
BadgeList.args = {
badgeType: "info",
maxItems: 3,
items: ["Badge 1", "Badge 2", "Badge 3", "Badge 4", "Badge 5"],
};

View File

@@ -1,2 +1,2 @@
export * from "./badge.directive";
export { BadgeDirective } from "./badge.directive";
export * from "./badge.module";

View File

@@ -9,10 +9,14 @@ export class I18nMockService implements I18nService {
collator: Intl.Collator;
localeNames: Map<string, string>;
constructor(private lookupTable: Record<string, string>) {}
constructor(private lookupTable: Record<string, string | ((...args: string[]) => string)>) {}
t(id: string, p1?: string, p2?: string, p3?: string) {
return this.lookupTable[id];
const value = this.lookupTable[id];
if (typeof value == "string") {
return value;
}
return value(p1, p2, p3);
}
translate(id: string, p1?: string, p2?: string, p3?: string) {