Skip to content

Commit f3b3966

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/integration/ng14/postcss-and-angular-devkit/build-angular-8.4.31
2 parents 98b1697 + dd30d81 commit f3b3966

11 files changed

+731
-0
lines changed

src/carbon.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ assets:
113113
name: Storybook
114114
action: link
115115
url: https://angular.carbondesignsystem.com/?path=/story/components-combobox
116+
contained-list:
117+
status: stable
118+
framework: angular
119+
externalDocsUrl: https://carbondesignsystem.com/components/contained-list/usage/
120+
demoLinks:
121+
- type: storybook
122+
name: Storybook
123+
action: link
124+
url: https://angular.carbondesignsystem.com/?path=/story/components-contained-list
116125
content-switcher:
117126
status: stable
118127
framework: angular
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
EventEmitter,
5+
HostBinding,
6+
Input,
7+
Output,
8+
TemplateRef
9+
} from "@angular/core";
10+
11+
@Component({
12+
selector: "cds-contained-list-item, ibm-contained-list-item",
13+
template: `
14+
<ng-container *ngIf="clickable">
15+
<button
16+
class="cds--contained-list-item__content"
17+
type="button"
18+
[disabled]="disabled"
19+
(click)="onClick()">
20+
<ng-content select="[ibmContainedListItemButton]"></ng-content>
21+
</button>
22+
</ng-container>
23+
<ng-container *ngIf="!clickable">
24+
<div class="cds--contained-list-item__content">
25+
<div *ngIf="icon" class="cds--contained-list-item__icon">
26+
<ng-container *ngIf="!isTemplate(icon)"><svg [ibmIcon]="icon" size="16"></svg></ng-container>
27+
<ng-template *ngIf="isTemplate(icon)" [ngTemplateOutlet]="icon"></ng-template>
28+
</div>
29+
<ng-content></ng-content>
30+
</div>
31+
</ng-container>
32+
<div class="cds--contained-list-item__action" *ngIf="action">
33+
<ng-template [ngTemplateOutlet]="action"></ng-template>
34+
</div>
35+
`,
36+
changeDetection: ChangeDetectionStrategy.OnPush
37+
})
38+
export class ContainedListItem {
39+
/**
40+
* A slot for a possible interactive element to render within the item.
41+
*/
42+
@Input() action: TemplateRef<any>;
43+
44+
/**
45+
* Whether this item is disabled.
46+
*/
47+
@Input() disabled = false;
48+
49+
/**
50+
* Whether this item is clickable.
51+
*/
52+
@Input() clickable: boolean;
53+
54+
/**
55+
* Provide an optional icon to render in front of the item's content.
56+
*
57+
* Note that if you intend to use this as a string ref, it's important to remember
58+
* to register the icon that you wish to add. In this case, it's also worth noting
59+
* that only icons with a size of 16 are currently supported.
60+
*/
61+
@Input() icon: TemplateRef<any> | string;
62+
63+
/**
64+
* Emits click event.
65+
*/
66+
@Output() click = new EventEmitter<void>();
67+
68+
/**
69+
* Host binding item class.
70+
*/
71+
@HostBinding("class.cds--contained-list-item") itemClass = true;
72+
73+
/**
74+
* Host binding item role attribute
75+
*/
76+
@HostBinding("attr.role") role = "listitem";
77+
78+
/**
79+
* Host binding clickable item class.
80+
*/
81+
@HostBinding("class.cds--contained-list-item--clickable") get itemClickableClass() {
82+
return this.clickable;
83+
}
84+
85+
/**
86+
* Host binding item with icon class.
87+
*/
88+
@HostBinding("class.cds--contained-list-item--with-icon") get itemWithIconClass() {
89+
return !!this.icon;
90+
}
91+
92+
public onClick() {
93+
this.click.emit();
94+
}
95+
96+
public isTemplate(value: string | TemplateRef<any>) {
97+
return value instanceof TemplateRef;
98+
}
99+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { Component } from "@angular/core";
2+
import { ComponentFixture, TestBed } from "@angular/core/testing";
3+
import { By } from "@angular/platform-browser";
4+
import { ButtonModule } from "../button";
5+
import { IconModule, IconService } from "../icon";
6+
import { ContainedListItem } from "./contained-list-item.component";
7+
import Apple16 from "@carbon/icons/es/apple/16";
8+
import Fish16 from "@carbon/icons/es/fish/16";
9+
import { ContainedList } from "./contained-list.component";
10+
import { ContainedListKind, ContainedListSize } from "./contained-list.enums";
11+
12+
@Component({
13+
template: `
14+
<ng-template #label>
15+
<h1>My contained list</h1>
16+
</ng-template>
17+
18+
<ng-template #action>
19+
<ibm-icon-button
20+
type="button"
21+
kind="primary"
22+
align="left"
23+
description="Add">
24+
<svg class="cds--btn__icon" ibmIcon="add" size="16"></svg>
25+
</ibm-icon-button>
26+
</ng-template>
27+
28+
<ng-template #icon>
29+
<svg ibmIcon="fish" size="16"></svg>
30+
</ng-template>
31+
32+
<cds-contained-list [label]="label" [action]="action">
33+
<cds-contained-list-item>List item</cds-contained-list-item>
34+
<cds-contained-list-item [icon]="icon">List item with icon</cds-contained-list-item>
35+
<cds-contained-list-item icon="apple">List item with string ref icon</cds-contained-list-item>
36+
<cds-contained-list-item [action]="action">List item with action</cds-contained-list-item>
37+
<cds-contained-list-item #clickableListItem [clickable]="true">
38+
<ng-container ibmContainedListItemButton>Clickable list item</ng-container>
39+
</cds-contained-list-item>
40+
</cds-contained-list>
41+
`
42+
})
43+
class WrapperComponent {
44+
constructor(private iconService: IconService) {
45+
this.iconService.registerAll([Apple16, Fish16]);
46+
}
47+
}
48+
49+
describe("ContainedList", () => {
50+
let component: ContainedList;
51+
let fixture: ComponentFixture<ContainedList>;
52+
53+
beforeEach(async () => {
54+
await TestBed.configureTestingModule({
55+
declarations: [ContainedList, ContainedListItem, WrapperComponent],
56+
imports: [IconModule, ButtonModule]
57+
}).compileComponents();
58+
59+
fixture = TestBed.createComponent(ContainedList);
60+
component = fixture.componentInstance;
61+
});
62+
63+
it("should set default inputs", () => {
64+
fixture.detectChanges();
65+
expect(component.action).toBeUndefined();
66+
expect(component.isInset).toBeFalsy();
67+
expect(component.kind).toBe(ContainedListKind.OnPage);
68+
expect(component.label).toBeUndefined();
69+
expect(component.size).toBe(ContainedListSize.Large);
70+
});
71+
72+
it("should display the label when a string is provided", () => {
73+
const label = "My contained list";
74+
component.label = label;
75+
fixture.detectChanges();
76+
77+
const labelElement = fixture.nativeElement.querySelector(".cds--contained-list__label");
78+
expect(labelElement.textContent.trim()).toEqual(label);
79+
});
80+
81+
it("should have the correct isInset class", () => {
82+
component.isInset = true;
83+
fixture.detectChanges();
84+
85+
const listElement: HTMLElement = fixture.nativeElement.querySelector(".cds--contained-list");
86+
expect(listElement).toHaveClass("cds--contained-list--inset-rulers");
87+
});
88+
89+
it("should have the correct size class", () => {
90+
component.size = ContainedListSize.Small;
91+
fixture.detectChanges();
92+
93+
const listElement: HTMLElement = fixture.nativeElement.querySelector(".cds--contained-list");
94+
expect(listElement).toHaveClass("cds--contained-list--sm");
95+
});
96+
97+
it("should have the correct kind class", () => {
98+
component.kind = ContainedListKind.Disclosed;
99+
fixture.detectChanges();
100+
101+
const listElement: HTMLElement = fixture.nativeElement.querySelector(".cds--contained-list");
102+
expect(listElement).toHaveClass("cds--contained-list--disclosed");
103+
});
104+
105+
describe("TemplateRefs", () => {
106+
it("should render the label if it is a TemplateRef", () => {
107+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
108+
wrapperFixture.detectChanges();
109+
110+
const labelRefElement = wrapperFixture.nativeElement.querySelector(".cds--contained-list .cds--contained-list__label h1");
111+
expect(labelRefElement.textContent.trim()).toBe("My contained list");
112+
});
113+
114+
it("should render the action if it is a TemplateRef", () => {
115+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
116+
wrapperFixture.detectChanges();
117+
118+
const actionElementRef = wrapperFixture.nativeElement.querySelector(".cds--contained-list .cds--contained-list__action ibm-icon-button");
119+
expect(actionElementRef).toBeTruthy();
120+
});
121+
});
122+
123+
describe("ContainedListItem", () => {
124+
it("should render the content", () => {
125+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
126+
wrapperFixture.detectChanges();
127+
128+
const listItemElement = wrapperFixture.debugElement.query(By.css(".cds--contained-list-item:nth-child(1)"));
129+
expect(listItemElement.nativeElement.textContent.trim()).toBe("List item");
130+
});
131+
132+
it("should render the icon", () => {
133+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
134+
wrapperFixture.detectChanges();
135+
136+
const iconElement = wrapperFixture.debugElement.query(By.css(".cds--contained-list-item:nth-child(2) svg[ibmIcon='fish']"));
137+
expect(iconElement).toBeTruthy();
138+
});
139+
140+
it("should render the icon", () => {
141+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
142+
wrapperFixture.detectChanges();
143+
144+
const iconElement = wrapperFixture.debugElement.query(By.css(".cds--contained-list-item:nth-child(3) svg[ng-reflect-ibm-icon='apple']"));
145+
expect(iconElement).toBeTruthy();
146+
});
147+
148+
it("should render the action", () => {
149+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
150+
wrapperFixture.detectChanges();
151+
152+
const actionElement = wrapperFixture.debugElement.query(By.css(".cds--contained-list-item:nth-child(4) ibm-icon-button"));
153+
expect(actionElement).toBeTruthy();
154+
});
155+
156+
it("should render with the clickable state", () => {
157+
const wrapperFixture: ComponentFixture<WrapperComponent> = TestBed.createComponent(WrapperComponent);
158+
wrapperFixture.detectChanges();
159+
160+
const clickableListItemElement = wrapperFixture.debugElement.query(By.css(".cds--contained-list-item:nth-child(5)"));
161+
expect(clickableListItemElement.nativeElement).toHaveClass("cds--contained-list-item--clickable");
162+
163+
const buttonElement = clickableListItemElement.nativeElement.querySelector("button");
164+
expect(buttonElement.textContent.trim()).toBe("Clickable list item");
165+
});
166+
});
167+
});
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
HostBinding,
5+
Input,
6+
TemplateRef
7+
} from "@angular/core";
8+
import { ContainedListKind, ContainedListSize } from "./contained-list.enums";
9+
10+
@Component({
11+
selector: "cds-contained-list, ibm-contained-list",
12+
template: `
13+
<div
14+
class="cds--contained-list"
15+
[ngClass]="{
16+
'cds--contained-list--inset-rulers': isInset,
17+
'cds--contained-list--on-page': kind === ContainedListKind.OnPage,
18+
'cds--contained-list--disclosed': kind === ContainedListKind.Disclosed,
19+
'cds--contained-list--sm': size === ContainedListSize.Small,
20+
'cds--contained-list--md': size === ContainedListSize.Medium,
21+
'cds--contained-list--lg': size === ContainedListSize.Large,
22+
'cds--contained-list--xl': size === ContainedListSize.ExtraLarge
23+
}">
24+
<div class="cds--contained-list__header">
25+
<div [id]="labelId" class="cds--contained-list__label">
26+
<ng-container *ngIf="!isTemplate(label)">{{ label }}</ng-container>
27+
<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
28+
</div>
29+
30+
<div class="cds--contained-list__action" *ngIf="action">
31+
<ng-template [ngTemplateOutlet]="action"></ng-template>
32+
</div>
33+
</div>
34+
<div role="list" [attr.aria-labelledby]="labelId">
35+
<ng-content></ng-content>
36+
</div>
37+
</div>
38+
`,
39+
changeDetection: ChangeDetectionStrategy.OnPush
40+
})
41+
export class ContainedList {
42+
/** Used to generate unique IDs */
43+
private static count = 0;
44+
45+
/**
46+
* A slot for a possible interactive element to render within the list header.
47+
*/
48+
@Input() action: TemplateRef<any>;
49+
50+
/**
51+
* Specify whether the dividing lines in between list items should be inset.
52+
*/
53+
@Input() isInset = false;
54+
55+
/**
56+
* The kind of ContainedList you want to display.
57+
*/
58+
@Input() kind: ContainedListKind = ContainedListKind.OnPage;
59+
60+
/**
61+
* A label describing the contained list.
62+
*/
63+
@Input() label: string | TemplateRef<any>;
64+
65+
/**
66+
* Specify the size of the contained list.
67+
*/
68+
@Input() size: ContainedListSize = ContainedListSize.Large;
69+
70+
/**
71+
* Label id for the contained list.
72+
*/
73+
@Input() labelId = `contained-list-${ContainedList.count++}-header`;
74+
75+
/**
76+
* Exposing ContainedListSize enum to the template
77+
*/
78+
public ContainedListSize: typeof ContainedListSize = ContainedListSize;
79+
80+
/**
81+
* Exposing ContainedListKind enum to the template
82+
*/
83+
public ContainedListKind: typeof ContainedListKind = ContainedListKind;
84+
85+
public isTemplate(value: string | TemplateRef<any>) {
86+
return value instanceof TemplateRef;
87+
}
88+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export enum ContainedListSize {
2+
Small = "sm",
3+
Medium = "md",
4+
Large = "lg",
5+
ExtraLarge = "xl"
6+
}
7+
8+
export enum ContainedListKind {
9+
OnPage = "on-page",
10+
Disclosed = "disclosed"
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { NgModule } from "@angular/core";
2+
import { CommonModule } from "@angular/common";
3+
import { ContainedList } from "./contained-list.component";
4+
import { ContainedListItem } from "./contained-list-item.component";
5+
import { IconModule } from "carbon-components-angular/icon";
6+
7+
@NgModule({
8+
declarations: [ContainedList, ContainedListItem],
9+
exports: [ContainedList, ContainedListItem],
10+
imports: [CommonModule, IconModule]
11+
})
12+
export class ContainedListModule {}

0 commit comments

Comments
 (0)