Skip to content

Commit

Permalink
Merge branch 'master' into feat/table-expand-all
Browse files Browse the repository at this point in the history
  • Loading branch information
Akshat55 authored Oct 8, 2024
2 parents 28f62e5 + 75422dc commit 3e3be1e
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/checkbox/checkbox-exported-tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from "chai";
import merge from "lodash/merge";
import merge from "lodash-es/merge";
import ComponentTests from "../exported-tests/component-tests";

const defaults = {
Expand Down
4 changes: 2 additions & 2 deletions src/combobox/combobox.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit, OnD
return this._clearSelectionAria.value;
}
static comboBoxCount = 0;
@Input() id = `dropdown-${ComboBox.comboBoxCount++}`;
@Input() labelId = `dropdown-label-${ComboBox.comboBoxCount++}`;
@Input() id = `combobox-${ComboBox.comboBoxCount++}`;
@Input() labelId = `combobox-label-${ComboBox.comboBoxCount++}`;
/**
* List of items to fill the content with.
*
Expand Down
4 changes: 4 additions & 0 deletions src/input/text-area.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ export class TextArea {
@HostBinding("class.cds--text-area--light") get isLightTheme() {
return this.theme === "light";
}

@HostBinding("attr.data-invalid") get getInvalidAttr() {
return this.invalid ? true : undefined;
}
}
148 changes: 96 additions & 52 deletions src/input/textarea-label.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,58 +31,89 @@ import { TextArea } from "./text-area.directive";
@Component({
selector: "cds-textarea-label, ibm-textarea-label",
template: `
<label
[for]="labelInputID"
[attr.aria-label]="ariaLabel"
class="cds--label"
[ngClass]="{
'cds--label--disabled': disabled,
'cds--skeleton': skeleton
}">
<ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template>
<ng-template #labelContent>
<ng-content></ng-content>
</ng-template>
</label>
<div
class="cds--text-area__wrapper"
[ngClass]="{
'cds--text-input__field-wrapper--warning': warn
}"
[attr.data-invalid]="(invalid ? true : null)"
#wrapper>
<svg
*ngIf="invalid"
cdsIcon="warning--filled"
size="16"
class="cds--text-area__invalid-icon">
</svg>
<svg
*ngIf="!invalid && warn"
cdsIcon="warning--alt--filled"
size="16"
class="cds--text-input__invalid-icon cds--text-input__invalid-icon--warning">
</svg>
<ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template>
<ng-template #textAreaContent>
<ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content>
</ng-template>
</div>
<div
*ngIf="!skeleton && helperText && !invalid && !warn"
class="cds--form__helper-text"
[ngClass]="{'cds--form__helper-text--disabled': disabled}">
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
</div>
<div *ngIf="invalid" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
</div>
<div *ngIf="!invalid && warn" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
</div>
<ng-container *ngIf="skeleton">
<span class="cds--label cds--skeleton"></span>
<div class="cds--text-area cds--skeleton"></div>
</ng-container>
<ng-container *ngIf="!skeleton">
<div class="cds--text-area__label-wrapper">
<label
[for]="labelInputID"
[attr.aria-label]="ariaLabel"
class="cds--label"
[ngClass]="{
'cds--label--disabled': disabled
}">
<ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template>
<ng-template #labelContent>
<ng-content></ng-content>
</ng-template>
</label>
</div>
<div
class="cds--text-area__wrapper"
[ngClass]="{
'cds--text-area__wrapper--warn': warn
}"
[attr.data-invalid]="(invalid ? true : null)"
#wrapper>
<svg
*ngIf="!fluid && invalid"
cdsIcon="warning--filled"
size="16"
class="cds--text-area__invalid-icon">
</svg>
<svg
*ngIf="!fluid && !invalid && warn"
cdsIcon="warning--alt--filled"
size="16"
class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning">
</svg>
<ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template>
<ng-template #textAreaContent>
<ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content>
</ng-template>
<ng-container *ngIf="fluid">
<hr class="cds--text-area__divider" />
<div *ngIf="invalid" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
<svg
cdsIcon="warning--filled"
size="16"
class="cds--text-area__invalid-icon">
</svg>
</div>
<div *ngIf="!invalid && warn" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
<svg
cdsIcon="warning--alt--filled"
size="16"
class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning">
</svg>
</div>
</ng-container>
</div>
<ng-container *ngIf="!fluid">
<div
*ngIf="helperText && !invalid && !warn"
class="cds--form__helper-text"
[ngClass]="{'cds--form__helper-text--disabled': disabled}">
<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
</div>
<div *ngIf="invalid" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container>
<ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template>
</div>
<div *ngIf="!invalid && warn" class="cds--form-requirement">
<ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container>
<ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template>
</div>
</ng-container>
</ng-container>
`
})
export class TextareaLabelComponent implements AfterViewInit {
Expand Down Expand Up @@ -136,6 +167,11 @@ export class TextareaLabelComponent implements AfterViewInit {
*/
@Input() ariaLabel: string;

/**
* Experimental: enable fluid state
*/
@Input() fluid = false;

// @ts-ignore
@ViewChild("wrapper", { static: false }) wrapper: ElementRef<HTMLDivElement>;

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

@HostBinding("class.cds--text-area--fluid") get fluidClass() {
return this.fluid && !this.skeleton;
}

@HostBinding("class.cds--text-area--fluid__skeleton") get fluidSkeletonClass() {
return this.fluid && this.skeleton;
}

/**
* Creates an instance of Label.
*/
Expand Down
59 changes: 34 additions & 25 deletions src/input/textarea.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* tslint:disable variable-name */

import { moduleMetadata, Meta } from "@storybook/angular";
import { Meta, moduleMetadata } from "@storybook/angular";
import { InputModule, TextareaLabelComponent } from "./";

export default {
Expand All @@ -10,6 +10,33 @@ export default {
imports: [InputModule]
})
],
args: {
disabled: false,
invalid: false,
invalidText: "Invalid entry",
warn: false,
warnText: "This is a warning!",
label: "Text input label",
helperText: "Optional helper text",
placeholder: "Placeholder",
cols: 50,
rows: 4,
autocomplete: "on",
theme: "dark",
readonly: false,
fluid: false,
skeleton: false
},
argTypes: {
autocomplete: {
options: ["on", "off"],
control: "radio"
},
theme: {
options: ["light", "dark"],
control: "radio"
}
},
component: TextareaLabelComponent
} as Meta;

Expand All @@ -21,6 +48,8 @@ const Template = (args) => ({
[invalid]="invalid"
[disabled]="disabled"
[invalidText]="invalidText"
[fluid]="fluid"
[skeleton]="skeleton"
[warn]="warn"
[warnText]="warnText">
{{label}}
Expand All @@ -38,30 +67,10 @@ const Template = (args) => ({
`
});
export const Basic = Template.bind({});
Basic.args = {
disabled: false,
invalid: false,
invalidText: "Invalid entry",
warn: false,
warnText: "This is a warning!",
label: "Text input label",
helperText: "Optional helper text",
placeholder: "Placeholder",
cols: 50,
rows: 4,
autocomplete: "on",
theme: "dark",
readonly: false
};
Basic.argTypes = {
autocomplete: {
options: ["on", "off"],
control: "radio"
},
theme: {
options: ["light", "dark"],
control: "radio"
}

export const Fluid = Template.bind({});
Fluid.args = {
fluid: true
};

const SkeletonTemplate = (args) => ({
Expand Down
26 changes: 25 additions & 1 deletion src/pagination/pagination.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TestBed } from "@angular/core/testing";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { Component, OnInit } from "@angular/core";
Expand Down Expand Up @@ -150,4 +150,28 @@ describe("Pagination", () => {
expect(buttonBackward.disabled).toBe(true);
expect(element.componentInstance.currentPage).toBe(5);
});

/**
* Number of pages should always be 1 even if totalDataLength is greater than 0
*/
it("should recalculate pages when changing data", () => {
const fixture = TestBed.createComponent(Pagination);
const wrapper = fixture.componentInstance;
const model = new PaginationModel();
model.currentPage = 1;
model.pageLength = 5;
model.totalDataLength = 9;
wrapper.model = model;
fixture.detectChanges();
expect(wrapper.pageOptions).toEqual(Array(2));
model.totalDataLength = 2;
fixture.detectChanges();
expect(wrapper.pageOptions).toEqual(Array(1));
model.totalDataLength = 20;
fixture.detectChanges();
expect(wrapper.pageOptions).toEqual(Array(4));
model.totalDataLength = 0;
fixture.detectChanges();
expect(wrapper.pageOptions).toEqual(Array(1));
});
});
10 changes: 8 additions & 2 deletions src/pagination/pagination.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,14 @@ export class Pagination {
}

get pageOptions() {
if (this.totalDataLength && this._pageOptions.length !== this.totalDataLength) {
this._pageOptions = Array(Math.ceil(this.totalDataLength / this.itemsPerPage));
/**
* Calculate number of pages based on totalDataLength and itemsPerPage.
* Even if totalDataLength is 0, numberOfPages should be always at least 1.
* New array will be constructed only if number of pages changes.
*/
const numberOfPages = Math.max(Math.ceil(this.totalDataLength / this.itemsPerPage), 1);
if (this._pageOptions.length !== numberOfPages) {
this._pageOptions = Array(numberOfPages);
}
return this._pageOptions;
}
Expand Down
15 changes: 11 additions & 4 deletions src/search/search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@
'cds--search--md': size === 'md',
'cds--search--lg': size === 'lg',
'cds--search--light': theme === 'light',
'cds--skeleton': skeleton,
'cds--skeleton': skeleton && !fluid,
'cds--search--expandable': expandable && !tableSearch,
'cds--search--expanded': expandable && !tableSearch && active,
'cds--toolbar-search': toolbar && !expandable,
'cds--toolbar-search--active': toolbar && !expandable && active,
'cds--toolbar-search-container-persistent': tableSearch && !expandable,
'cds--toolbar-search-container-expandable': tableSearch && expandable,
'cds--toolbar-search-container-active': tableSearch && expandable && active
'cds--toolbar-search-container-active': tableSearch && expandable && active,
'cds--search--fluid': fluid,
'cds--search--disabled': disabled
}"
role="search"
[attr.aria-label]="ariaLabel"
(click)="openSearch()">
<label class="cds--label" [for]="id">{{label}}</label>
<label
class="cds--label"
[for]="id"
[ngClass]="{ 'cds--skeleton': skeleton && fluid }">
{{ !skeleton ? label : ''}}
</label>

<div *ngIf="skeleton; else enableInput" class="cds--search-input"></div>
<div *ngIf="skeleton; else enableInput" class="cds--text-input cds--skeleton"></div>
<ng-template #enableInput>
<input
#input
Expand Down
8 changes: 8 additions & 0 deletions src/search/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class Search implements ControlValueAccessor {
return !(this.toolbar || this.expandable);
}

@HostBinding("class.cds--text-input--fluid__skeleton") get fluidSkeletonClass() {
return this.skeleton && this.fluid;
}

/**
* @deprecated since v5 - Use `cdsLayer` directive instead
* `light` or `dark` search theme.
Expand Down Expand Up @@ -118,6 +122,10 @@ export class Search implements ControlValueAccessor {
* Sets the aria label on the `div` element with the `search` role.
*/
@Input() ariaLabel: string;
/**
* Experimental: enable fluid state
*/
@Input() fluid = false;
/**
* Emits an event when value is changed.
*/
Expand Down
Loading

0 comments on commit 3e3be1e

Please sign in to comment.