Skip to content

Commit

Permalink
Merge pull request #3055 from Akshat55/tag-components
Browse files Browse the repository at this point in the history
feat: introduce selectable & operational tags
  • Loading branch information
zvonimirfras authored Nov 14, 2024
2 parents a119742 + f25bfaa commit 6b449df
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 28 deletions.
3 changes: 3 additions & 0 deletions src/tag/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export * from "./tag-filter.component";
export * from "./tag.component";
export * from "./tag-icon.directive";
export * from "./tag-selectable.component";
export * from "./tag-operational.component";
export * from "./tag.module";
4 changes: 4 additions & 0 deletions src/tag/tag-filter.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe("TagFilter", () => {

it("should call onClick on label click", () => {
fixture = TestBed.createComponent(TagFilterTest);
fixture.detectChanges();
debugElement = fixture.debugElement.query(By.css(".cds--tag__label"));
fixture.detectChanges();
spyOn(debugElement.componentInstance.click, "emit");
Expand All @@ -39,6 +40,7 @@ describe("TagFilter", () => {

it("should call both onClick and onClose on close icon click", () => {
fixture = TestBed.createComponent(TagFilterTest);
fixture.detectChanges();
debugElement = fixture.debugElement.query(By.css(".cds--tag__close-icon"));
fixture.detectChanges();
spyOn(debugElement.componentInstance.close, "emit");
Expand All @@ -52,6 +54,7 @@ describe("TagFilter", () => {
it("should not call onClick when disabled", () => {
fixture = TestBed.createComponent(TagFilterTest);
fixture.componentInstance.disabled = true;
fixture.detectChanges();
debugElement = fixture.debugElement.query(By.css(".cds--tag__label"));
fixture.detectChanges();
spyOn(debugElement.componentInstance.click, "emit");
Expand All @@ -63,6 +66,7 @@ describe("TagFilter", () => {
it("should not call onClick nor onClose when disabled", () => {
fixture = TestBed.createComponent(TagFilterTest);
fixture.componentInstance.disabled = true;
fixture.detectChanges();
debugElement = fixture.debugElement.query(By.css(".cds--tag__close-icon"));
fixture.detectChanges();
spyOn(debugElement.componentInstance.close, "emit");
Expand Down
46 changes: 29 additions & 17 deletions src/tag/tag-filter.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@ import {
Output,
EventEmitter,
HostBinding,
Input
Input,
TemplateRef
} from "@angular/core";
import { Tag } from "./tag.component";

@Component({
selector: "cds-tag-filter, ibm-tag-filter",
template: `
<span
class="cds--tag__label"
[attr.title]="title ? title : null"
(click)="onClick($event)">
<ng-content></ng-content>
</span>
<button
class="cds--tag__close-icon"
(click)="onClose($event)"
[disabled]="disabled"
[title]="closeButtonLabel">
<span class="cds--visually-hidden">{{closeButtonLabel}}</span>
<svg cdsIcon="close" size="16"></svg>
</button>
<ng-container *ngIf="!skeleton">
<ng-content select="[cdsTagIcon],[ibmTagIcon]"></ng-content>
<span
class="cds--tag__label"
[attr.title]="title ? title : null"
(click)="onClick($event)">
<ng-content></ng-content>
</span>
<button
class="cds--tag__close-icon"
(click)="onClose($event)"
[disabled]="disabled"
[title]="closeButtonLabel">
<span class="cds--visually-hidden">{{closeButtonLabel}}</span>
<svg cdsIcon="close" size="16"></svg>
</button>
</ng-container>
`
})
export class TagFilter extends Tag {
@Input() closeButtonLabel = "Clear Filter";
@Input() disabled: boolean;
@Input() disabled = false;
@Input() title: string;

/**
Expand Down Expand Up @@ -59,8 +63,16 @@ export class TagFilter extends Tag {
this.close.emit();
}

/**
* @todo
* Remove `cds--tag--${this.size}` in v7
*/
@HostBinding("attr.class") get attrClass() {
return `cds--tag cds--tag--filter cds--tag--${this.type} cds--tag--${this.size} cds--layout--size-${this.size} ${this.class}${this.disabled ? " cds--tag--disabled" : ""}`;
const disabledClass = this.disabled ? "cds--tag--disabled" : "";
const sizeClass = `cds--tag--${this.size} cds--layout--size-${this.size}`;
const skeletonClass = this.skeleton ? "cds--skeleton" : "";

return `cds--tag cds--tag--filter cds--tag--${this.type} ${disabledClass} ${sizeClass} ${skeletonClass} ${this.class}`;
}

@HostBinding("attr.aria-label") get attrAriaLabel() {
Expand Down
8 changes: 8 additions & 0 deletions src/tag/tag-icon.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Directive, HostBinding } from "@angular/core";

@Directive({
selector: "[cdsTagIcon], [ibmTagIcon]"
})
export class TagIconDirective {
@HostBinding("class.cds--tag__custom-icon") tagIcon = true;
}
39 changes: 39 additions & 0 deletions src/tag/tag-operational.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
ChangeDetectionStrategy,
Component,
HostBinding,
Input
} from "@angular/core";
import { Tag } from "./tag.component";

@Component({
selector: "cds-tag-operational, ibm-tag-operational",
template: `
<ng-container *ngIf="!skeleton">
<ng-content select="[cdsTagIcon],[ibmTagIcon]"></ng-content>
<span class="cds--tag__label">
<ng-content></ng-content>
</span>
</ng-container>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TagOperationalComponent extends Tag {
@HostBinding("attr.role") role = "button";
@HostBinding("attr.type") buttonType = "button";
@HostBinding("attr.tabindex") tabIndex = 0;

@Input() disabled = false;

/**
* @todo
* Remove `cds--tag--${this.size}` in v7
*/
@HostBinding("attr.class") get attrClass() {
const disabledClass = this.disabled ? "cds--tag--disabled" : "";
const sizeClass = `cds--tag--${this.size} cds--layout--size-${this.size}`;
const skeletonClass = this.skeleton ? "cds--skeleton" : "";

return `cds--tag cds--tag--operational cds--tag--${this.type} ${disabledClass} ${sizeClass} ${skeletonClass} ${this.class}`;
}
}
57 changes: 57 additions & 0 deletions src/tag/tag-selectable.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
HostBinding,
HostListener,
Input,
Output
} from "@angular/core";

@Component({
selector: "cds-tag-selectable, ibm-tag-selectable",
template: `
<ng-container *ngIf="!skeleton">
<ng-content select="[cdsTagIcon],[ibmTagIcon]"></ng-content>
<span class="cds--tag__label">
<ng-content></ng-content>
</span>
</ng-container>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TagSelectableComponent {
@HostBinding("attr.role") role = "button";
@HostBinding("attr.type") buttonType = "button";
@HostBinding("attr.tabindex") tabIndex = 0;
@HostBinding("attr.aria-pressed") get ariaPressed() {
return this.selected;
}

@Input() size: "sm" | "md" | "lg" = "md";
@Input() skeleton = false;
@Input() disabled = false;
@Input() class = "";
@Input() selected = false;

@Output() selectedChange = new EventEmitter<boolean>();

@HostListener("click")
onClick() {
this.selected = !this.selected;
this.selectedChange.emit(this.selected);
}

/**
* @todo
* Remove `cds--tag--${this.size}` in v7
*/
@HostBinding("attr.class") get attrClass() {
const disabledClass = this.disabled ? "cds--tag--disabled" : "";
const sizeClass = `cds--tag--${this.size} cds--layout--size-${this.size}`;
const skeletonClass = this.skeleton ? "cds--skeleton" : "";
const selectedClass = this.selected ? "cds--tag--selectable-selected" : "";

return `cds--tag cds--tag--selectable ${selectedClass} ${disabledClass} ${sizeClass} ${skeletonClass} ${this.class}`;
}
}
22 changes: 19 additions & 3 deletions src/tag/tag.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ export type TagType = "red" |
*/
@Component({
selector: "cds-tag, ibm-tag",
template: `<ng-content></ng-content>`
template: `
<ng-container *ngIf="!skeleton">
<ng-content select="[cdsTagIcon],[ibmTagIcon]"></ng-content>
<span class="cds--tag__label">
<ng-content></ng-content>
</span>
</ng-container>
`
})
export class Tag {
/**
Expand All @@ -42,11 +49,20 @@ export class Tag {
/**
* Tag render size
*/
@Input() size: "sm" | "md" = "md";
@Input() size: "sm" | "md" | "lg" = "md";

@Input() class = "";

@Input() skeleton = false;

/**
* @todo
* Remove `cds--tag--${this.size}` in v7
*/
@HostBinding("attr.class") get attrClass() {
return `cds--tag cds--tag--${this.type} cds--tag--${this.size} cds--layout--size-${this.size} ${this.class}`;
const skeletonClass = this.skeleton ? "cds--skeleton" : "";
const sizeClass = `cds--tag--${this.size} cds--layout--size-${this.size}`;

return `cds--tag cds--tag--${this.type} ${sizeClass} ${skeletonClass} ${this.class}`;
}
}
23 changes: 19 additions & 4 deletions src/tag/tag.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ import { CommonModule } from "@angular/common";
import { Tag } from "./tag.component";
import { TagFilter } from "./tag-filter.component";
import { IconModule } from "carbon-components-angular/icon";
import { TagIconDirective } from "./tag-icon.directive";
import { TagSelectableComponent } from "./tag-selectable.component";
import { TagOperationalComponent } from "./tag-operational.component";

@NgModule({
declarations: [ Tag, TagFilter ],
exports: [ Tag, TagFilter ],
imports: [ CommonModule, IconModule ]
declarations: [
Tag,
TagFilter,
TagIconDirective,
TagSelectableComponent,
TagOperationalComponent
],
exports: [
Tag,
TagFilter,
TagIconDirective,
TagSelectableComponent,
TagOperationalComponent
],
imports: [CommonModule, IconModule]
})
export class TagModule { }
export class TagModule {}
Loading

0 comments on commit 6b449df

Please sign in to comment.