Skip to content

Commit 984f3c9

Browse files
committed
Merge branch 'feat/table-expand-all' of https://github.com/eduardmarcinco/carbon-components-angular into feat/table-expand-all
2 parents b7d1e67 + 3e3be1e commit 984f3c9

13 files changed

+265
-105
lines changed

src/checkbox/checkbox-exported-tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from "chai";
2-
import merge from "lodash/merge";
2+
import merge from "lodash-es/merge";
33
import ComponentTests from "../exported-tests/component-tests";
44

55
const defaults = {

src/combobox/combobox.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit, OnD
263263
return this._clearSelectionAria.value;
264264
}
265265
static comboBoxCount = 0;
266-
@Input() id = `dropdown-${ComboBox.comboBoxCount++}`;
267-
@Input() labelId = `dropdown-label-${ComboBox.comboBoxCount++}`;
266+
@Input() id = `combobox-${ComboBox.comboBoxCount++}`;
267+
@Input() labelId = `combobox-label-${ComboBox.comboBoxCount++}`;
268268
/**
269269
* List of items to fill the content with.
270270
*

src/input/text-area.directive.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ export class TextArea {
2727
@HostBinding("class.cds--text-area--light") get isLightTheme() {
2828
return this.theme === "light";
2929
}
30+
31+
@HostBinding("attr.data-invalid") get getInvalidAttr() {
32+
return this.invalid ? true : undefined;
33+
}
3034
}

src/input/textarea-label.component.ts

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,58 +31,89 @@ import { TextArea } from "./text-area.directive";
3131
@Component({
3232
selector: "cds-textarea-label, ibm-textarea-label",
3333
template: `
34-
<label
35-
[for]="labelInputID"
36-
[attr.aria-label]="ariaLabel"
37-
class="cds--label"
38-
[ngClass]="{
39-
'cds--label--disabled': disabled,
40-
'cds--skeleton': skeleton
41-
}">
42-
<ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template>
43-
<ng-template #labelContent>
44-
<ng-content></ng-content>
45-
</ng-template>
46-
</label>
47-
<div
48-
class="cds--text-area__wrapper"
49-
[ngClass]="{
50-
'cds--text-input__field-wrapper--warning': warn
51-
}"
52-
[attr.data-invalid]="(invalid ? true : null)"
53-
#wrapper>
54-
<svg
55-
*ngIf="invalid"
56-
cdsIcon="warning--filled"
57-
size="16"
58-
class="cds--text-area__invalid-icon">
59-
</svg>
60-
<svg
61-
*ngIf="!invalid && warn"
62-
cdsIcon="warning--alt--filled"
63-
size="16"
64-
class="cds--text-input__invalid-icon cds--text-input__invalid-icon--warning">
65-
</svg>
66-
<ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template>
67-
<ng-template #textAreaContent>
68-
<ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content>
69-
</ng-template>
70-
</div>
71-
<div
72-
*ngIf="!skeleton && helperText && !invalid && !warn"
73-
class="cds--form__helper-text"
74-
[ngClass]="{'cds--form__helper-text--disabled': disabled}">
75-
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
76-
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
77-
</div>
78-
<div *ngIf="invalid" class="cds--form-requirement">
79-
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
80-
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
81-
</div>
82-
<div *ngIf="!invalid && warn" class="cds--form-requirement">
83-
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
84-
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
85-
</div>
34+
<ng-container *ngIf="skeleton">
35+
<span class="cds--label cds--skeleton"></span>
36+
<div class="cds--text-area cds--skeleton"></div>
37+
</ng-container>
38+
<ng-container *ngIf="!skeleton">
39+
<div class="cds--text-area__label-wrapper">
40+
<label
41+
[for]="labelInputID"
42+
[attr.aria-label]="ariaLabel"
43+
class="cds--label"
44+
[ngClass]="{
45+
'cds--label--disabled': disabled
46+
}">
47+
<ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template>
48+
<ng-template #labelContent>
49+
<ng-content></ng-content>
50+
</ng-template>
51+
</label>
52+
</div>
53+
<div
54+
class="cds--text-area__wrapper"
55+
[ngClass]="{
56+
'cds--text-area__wrapper--warn': warn
57+
}"
58+
[attr.data-invalid]="(invalid ? true : null)"
59+
#wrapper>
60+
<svg
61+
*ngIf="!fluid && invalid"
62+
cdsIcon="warning--filled"
63+
size="16"
64+
class="cds--text-area__invalid-icon">
65+
</svg>
66+
<svg
67+
*ngIf="!fluid && !invalid && warn"
68+
cdsIcon="warning--alt--filled"
69+
size="16"
70+
class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning">
71+
</svg>
72+
<ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template>
73+
<ng-template #textAreaContent>
74+
<ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content>
75+
</ng-template>
76+
77+
<ng-container *ngIf="fluid">
78+
<hr class="cds--text-area__divider" />
79+
<div *ngIf="invalid" class="cds--form-requirement">
80+
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
81+
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
82+
<svg
83+
cdsIcon="warning--filled"
84+
size="16"
85+
class="cds--text-area__invalid-icon">
86+
</svg>
87+
</div>
88+
<div *ngIf="!invalid && warn" class="cds--form-requirement">
89+
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
90+
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
91+
<svg
92+
cdsIcon="warning--alt--filled"
93+
size="16"
94+
class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning">
95+
</svg>
96+
</div>
97+
</ng-container>
98+
</div>
99+
<ng-container *ngIf="!fluid">
100+
<div
101+
*ngIf="helperText && !invalid && !warn"
102+
class="cds--form__helper-text"
103+
[ngClass]="{'cds--form__helper-text--disabled': disabled}">
104+
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
105+
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
106+
</div>
107+
<div *ngIf="invalid" class="cds--form-requirement">
108+
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
109+
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
110+
</div>
111+
<div *ngIf="!invalid && warn" class="cds--form-requirement">
112+
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
113+
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
114+
</div>
115+
</ng-container>
116+
</ng-container>
86117
`
87118
})
88119
export class TextareaLabelComponent implements AfterViewInit {
@@ -136,6 +167,11 @@ export class TextareaLabelComponent implements AfterViewInit {
136167
*/
137168
@Input() ariaLabel: string;
138169

170+
/**
171+
* Experimental: enable fluid state
172+
*/
173+
@Input() fluid = false;
174+
139175
// @ts-ignore
140176
@ViewChild("wrapper", { static: false }) wrapper: ElementRef<HTMLDivElement>;
141177

@@ -148,6 +184,14 @@ export class TextareaLabelComponent implements AfterViewInit {
148184
return this.wrapper?.nativeElement.querySelector("textarea")?.readOnly ?? false;
149185
}
150186

187+
@HostBinding("class.cds--text-area--fluid") get fluidClass() {
188+
return this.fluid && !this.skeleton;
189+
}
190+
191+
@HostBinding("class.cds--text-area--fluid__skeleton") get fluidSkeletonClass() {
192+
return this.fluid && this.skeleton;
193+
}
194+
151195
/**
152196
* Creates an instance of Label.
153197
*/

src/input/textarea.stories.ts

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* tslint:disable variable-name */
22

3-
import { moduleMetadata, Meta } from "@storybook/angular";
3+
import { Meta, moduleMetadata } from "@storybook/angular";
44
import { InputModule, TextareaLabelComponent } from "./";
55

66
export default {
@@ -10,6 +10,33 @@ export default {
1010
imports: [InputModule]
1111
})
1212
],
13+
args: {
14+
disabled: false,
15+
invalid: false,
16+
invalidText: "Invalid entry",
17+
warn: false,
18+
warnText: "This is a warning!",
19+
label: "Text input label",
20+
helperText: "Optional helper text",
21+
placeholder: "Placeholder",
22+
cols: 50,
23+
rows: 4,
24+
autocomplete: "on",
25+
theme: "dark",
26+
readonly: false,
27+
fluid: false,
28+
skeleton: false
29+
},
30+
argTypes: {
31+
autocomplete: {
32+
options: ["on", "off"],
33+
control: "radio"
34+
},
35+
theme: {
36+
options: ["light", "dark"],
37+
control: "radio"
38+
}
39+
},
1340
component: TextareaLabelComponent
1441
} as Meta;
1542

@@ -21,6 +48,8 @@ const Template = (args) => ({
2148
[invalid]="invalid"
2249
[disabled]="disabled"
2350
[invalidText]="invalidText"
51+
[fluid]="fluid"
52+
[skeleton]="skeleton"
2453
[warn]="warn"
2554
[warnText]="warnText">
2655
{{label}}
@@ -38,30 +67,10 @@ const Template = (args) => ({
3867
`
3968
});
4069
export const Basic = Template.bind({});
41-
Basic.args = {
42-
disabled: false,
43-
invalid: false,
44-
invalidText: "Invalid entry",
45-
warn: false,
46-
warnText: "This is a warning!",
47-
label: "Text input label",
48-
helperText: "Optional helper text",
49-
placeholder: "Placeholder",
50-
cols: 50,
51-
rows: 4,
52-
autocomplete: "on",
53-
theme: "dark",
54-
readonly: false
55-
};
56-
Basic.argTypes = {
57-
autocomplete: {
58-
options: ["on", "off"],
59-
control: "radio"
60-
},
61-
theme: {
62-
options: ["light", "dark"],
63-
control: "radio"
64-
}
70+
71+
export const Fluid = Template.bind({});
72+
Fluid.args = {
73+
fluid: true
6574
};
6675

6776
const SkeletonTemplate = (args) => ({

src/pagination/pagination.component.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TestBed } from "@angular/core/testing";
1+
import { ComponentFixture, TestBed } from "@angular/core/testing";
22
import { By } from "@angular/platform-browser";
33
import { FormsModule } from "@angular/forms";
44
import { Component, OnInit } from "@angular/core";
@@ -150,4 +150,28 @@ describe("Pagination", () => {
150150
expect(buttonBackward.disabled).toBe(true);
151151
expect(element.componentInstance.currentPage).toBe(5);
152152
});
153+
154+
/**
155+
* Number of pages should always be 1 even if totalDataLength is greater than 0
156+
*/
157+
it("should recalculate pages when changing data", () => {
158+
const fixture = TestBed.createComponent(Pagination);
159+
const wrapper = fixture.componentInstance;
160+
const model = new PaginationModel();
161+
model.currentPage = 1;
162+
model.pageLength = 5;
163+
model.totalDataLength = 9;
164+
wrapper.model = model;
165+
fixture.detectChanges();
166+
expect(wrapper.pageOptions).toEqual(Array(2));
167+
model.totalDataLength = 2;
168+
fixture.detectChanges();
169+
expect(wrapper.pageOptions).toEqual(Array(1));
170+
model.totalDataLength = 20;
171+
fixture.detectChanges();
172+
expect(wrapper.pageOptions).toEqual(Array(4));
173+
model.totalDataLength = 0;
174+
fixture.detectChanges();
175+
expect(wrapper.pageOptions).toEqual(Array(1));
176+
});
153177
});

src/pagination/pagination.component.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,14 @@ export class Pagination {
320320
}
321321

322322
get pageOptions() {
323-
if (this.totalDataLength && this._pageOptions.length !== this.totalDataLength) {
324-
this._pageOptions = Array(Math.ceil(this.totalDataLength / this.itemsPerPage));
323+
/**
324+
* Calculate number of pages based on totalDataLength and itemsPerPage.
325+
* Even if totalDataLength is 0, numberOfPages should be always at least 1.
326+
* New array will be constructed only if number of pages changes.
327+
*/
328+
const numberOfPages = Math.max(Math.ceil(this.totalDataLength / this.itemsPerPage), 1);
329+
if (this._pageOptions.length !== numberOfPages) {
330+
this._pageOptions = Array(numberOfPages);
325331
}
326332
return this._pageOptions;
327333
}

src/search/search.component.html

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@
55
'cds--search--md': size === 'md',
66
'cds--search--lg': size === 'lg',
77
'cds--search--light': theme === 'light',
8-
'cds--skeleton': skeleton,
8+
'cds--skeleton': skeleton && !fluid,
99
'cds--search--expandable': expandable && !tableSearch,
1010
'cds--search--expanded': expandable && !tableSearch && active,
1111
'cds--toolbar-search': toolbar && !expandable,
1212
'cds--toolbar-search--active': toolbar && !expandable && active,
1313
'cds--toolbar-search-container-persistent': tableSearch && !expandable,
1414
'cds--toolbar-search-container-expandable': tableSearch && expandable,
15-
'cds--toolbar-search-container-active': tableSearch && expandable && active
15+
'cds--toolbar-search-container-active': tableSearch && expandable && active,
16+
'cds--search--fluid': fluid,
17+
'cds--search--disabled': disabled
1618
}"
1719
role="search"
1820
[attr.aria-label]="ariaLabel"
1921
(click)="openSearch()">
20-
<label class="cds--label" [for]="id">{{label}}</label>
22+
<label
23+
class="cds--label"
24+
[for]="id"
25+
[ngClass]="{ 'cds--skeleton': skeleton && fluid }">
26+
{{ !skeleton ? label : ''}}
27+
</label>
2128

22-
<div *ngIf="skeleton; else enableInput" class="cds--search-input"></div>
29+
<div *ngIf="skeleton; else enableInput" class="cds--text-input cds--skeleton"></div>
2330
<ng-template #enableInput>
2431
<input
2532
#input

src/search/search.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export class Search implements ControlValueAccessor {
4141
return !(this.toolbar || this.expandable);
4242
}
4343

44+
@HostBinding("class.cds--text-input--fluid__skeleton") get fluidSkeletonClass() {
45+
return this.skeleton && this.fluid;
46+
}
47+
4448
/**
4549
* @deprecated since v5 - Use `cdsLayer` directive instead
4650
* `light` or `dark` search theme.
@@ -118,6 +122,10 @@ export class Search implements ControlValueAccessor {
118122
* Sets the aria label on the `div` element with the `search` role.
119123
*/
120124
@Input() ariaLabel: string;
125+
/**
126+
* Experimental: enable fluid state
127+
*/
128+
@Input() fluid = false;
121129
/**
122130
* Emits an event when value is changed.
123131
*/

0 commit comments

Comments
 (0)