+
+
+
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.spec.ts b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.spec.ts
index 9abcaa063..52d3f2497 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.spec.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.spec.ts
@@ -1,30 +1,46 @@
import { render, fireEvent } from "@testing-library/angular";
import { DxcCardComponent } from "./dxc-card.component";
import { MatCardModule } from "@angular/material/card";
+import { DxcBoxModule } from "../dxc-box/dxc-box.module";
+import { TestBed } from "@angular/core/testing";
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting,
+} from "@angular/platform-browser-dynamic/testing";
+import { screen } from "@testing-library/dom";
+import { BackgroundProviderModule } from "../background-provider/background-provider.module";
+
+TestBed.initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
describe("DxcCardComponent tests", () => {
test("should render dxc-card", async () => {
const projection = "Content inside the ng-content!";
- const dxcCard = await render(DxcCardComponent, {
- template: `
${projection}`,
+ await render(`
${projection}`, {
+ imports: [BackgroundProviderModule, DxcBoxModule],
componentProperties: {},
- imports: [MatCardModule],
+ declarations: [DxcCardComponent],
});
- expect(dxcCard.getByText(projection));
+ expect(screen.getByText(projection));
});
test("dxc-card onClick", async () => {
const projection = "Content inside the ng-content!";
const onClickFunction = jest.fn();
- const dxcCard = await render(DxcCardComponent, {
- template: `
${projection}`,
- componentProperties: { onClickFunction },
- imports: [MatCardModule],
- });
+ await render(
+ `
${projection}`,
+ {
+ imports: [BackgroundProviderModule, DxcBoxModule],
+ componentProperties: { onClickFunction },
+ declarations: [DxcCardComponent],
+ }
+ );
- expect(dxcCard.getByText(projection));
- fireEvent.click(dxcCard.getByText(projection));
+ expect(screen.getByText(projection));
+ fireEvent.click(screen.getByText(projection));
expect(onClickFunction).toHaveBeenCalled();
});
});
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.ts
index 7b61d40be..1a39abd64 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.component.ts
@@ -9,8 +9,7 @@ import {
EventEmitter,
SimpleChanges,
ChangeDetectorRef,
- Inject,
- Optional
+ Optional,
} from "@angular/core";
import { css } from "emotion";
import { BehaviorSubject } from "rxjs";
@@ -20,6 +19,7 @@ import {
coerceNumberProperty,
} from "@angular/cdk/coercion";
import { BackgroundProviderService } from "../background-provider/service/background-provider.service";
+import { Space, Spacing, CardProperties } from "./dxc-card.types"
@Component({
selector: "dxc-card",
@@ -28,9 +28,52 @@ import { BackgroundProviderService } from "../background-provider/service/backgr
providers: [CssUtils],
})
export class DxcCardComponent implements OnInit {
+ /**
+ * URL of the image that will be placed in the card component.
+ */
@Input() imageSrc: string;
- @Input() imagePosition: string;
- @Input() imagePadding: any;
+
+ /**
+ * Color of the image background.
+ */
+ @Input() imageBgColor: string = "black";
+
+ /**
+ * Size of the padding to be applied to the image section of the component
+ * ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge').
+ * You can pass an object with 'top', 'bottom', 'left' and 'right' properties
+ * in order to specify different padding sizes.
+ */
+ @Input() imagePadding: Space | Spacing;
+
+ /**
+ * Whether the image should appear in relation to the content.
+ */
+ @Input() imagePosition: "after" | "before" = "before";
+
+ /**
+ * Size of the padding to be applied to the content section of the component
+ * ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge').
+ * You can pass an object with 'top', 'bottom', 'left' and 'right' properties
+ * in order to specify different padding sizes.
+ */
+ @Input() contentPadding: Space | Spacing;
+
+ /**
+ * If defined, the card will be displayed as an anchor, using this prop as "href".
+ * Component will show some visual feedback on hover.
+ */
+ @Input() linkHref: string;
+
+ /**
+ * This event will emit when the user clicks the card. Component will show some
+ * visual feedback on hover.
+ */
+ @Output() onClick: EventEmitter
= new EventEmitter();
+
+ /**
+ * Whether the image must cover the whole image area of the card.
+ */
@Input()
get imageCover(): boolean {
return this._imageCover;
@@ -39,9 +82,18 @@ export class DxcCardComponent implements OnInit {
this._imageCover = coerceBooleanProperty(value);
}
private _imageCover = false;
- @Input() imageBgColor: string;
- @Input() margin: any;
- @Input() linkHref: string;
+
+ /**
+ * Size of the margin to be applied to the component
+ * ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge').
+ * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in
+ * order to specify different margin sizes.
+ */
+ @Input() margin: Space | Spacing;
+
+ /**
+ * Value of the tabindex given when there is an href.
+ */
@Input()
get tabIndexValue(): number {
return this._tabIndexValue;
@@ -49,23 +101,30 @@ export class DxcCardComponent implements OnInit {
set tabIndexValue(value: number) {
this._tabIndexValue = coerceNumberProperty(value);
}
- private _tabIndexValue;
+ private _tabIndexValue = 0;
- @Output() onClick = new EventEmitter();
+ /**
+ * Whether the card must be outlined.
+ */
+ @Input() outlined: boolean = true;
+
+ private isHovered: boolean;
@HostBinding("class") className;
@ViewChild("content", { static: false }) content: ElementRef;
- defaultInputs = new BehaviorSubject({
+ defaultInputs = new BehaviorSubject({
imageSrc: null,
- imagePosition: "before",
+ imageBgColor: "black",
imagePadding: null,
+ imagePosition: "before",
+ contentPadding: null,
+ linkHref: null,
imageCover: false,
- imageBgColor: "black",
margin: null,
- linkHref: null,
tabIndexValue: 0,
+ outlined: true,
});
public ngOnChanges(changes: SimpleChanges): void {
@@ -80,15 +139,14 @@ export class DxcCardComponent implements OnInit {
this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
}
- constructor(private utils: CssUtils, private cdRef: ChangeDetectorRef,
- @Optional() @Inject("bgService") public bgProviderService: BackgroundProviderService) {
- }
+ constructor(
+ private utils: CssUtils,
+ private cdRef: ChangeDetectorRef,
+ @Optional() public bgProviderService?: BackgroundProviderService
+ ) {}
ngOnInit() {
this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
- this.bgProviderService?.$changeColor?.subscribe(resp => {
- console.log(resp);
- });
}
ngAfterContentChecked() {
@@ -110,20 +168,30 @@ export class DxcCardComponent implements OnInit {
applyTheme(href, outlined) {
return css`
- mat-card {
- background-color: var(--card-backgroundColor);
- color: black;
- ${!outlined ? this.utils.getBoxShadow("1") : this.utils.getBoxShadow(0)}
- }
+ mat-card {
+ ${this.utils.getBoxShadow(0, true)}
+ }
mat-card:hover {
- ${!outlined
- ? this.utils.getBoxShadow(this.getShadowDepthOnHover(href))
- : this.utils.getBoxShadow("1")}
+ ${this.utils.getBoxShadow(0, true)}
}
`;
}
+ changeIsHovered(isHovered: boolean) {
+ this.isHovered = isHovered;
+ }
+
+ getShadowDepth() {
+ return !this.defaultInputs.value.outlined
+ ? "0"
+ : this.isHovered &&
+ this.onClick.observers.length > 0 &&
+ this.linkHref !== ""
+ ? "2"
+ : "1";
+ }
+
getCursor(href) {
if (this.onClick.observers.length > 0 || href) {
return css`
@@ -145,19 +213,20 @@ export class DxcCardComponent implements OnInit {
getDynamicStyle(inputs) {
return css`
display: inline-flex;
+ ${this.utils.getMargins(inputs.margin)}
+ ${this.getCursor(inputs.linkHref)}
+ width: var(--card-width);
+ height: var(--card-height);
+
mat-card {
- ${this.utils.getMargins(inputs.margin)}
- ${this.getCursor(inputs.linkHref)}
- font-family: var(--fontFamily);
font-size: 14px;
display: inline-flex;
- width: 400px;
- height: 220px;
padding: 0px;
${this.tabIndexValue === -1 ? "outline:none;" : ""}
.content {
overflow: hidden;
width: 260px;
+ ${this.utils.getPaddings(inputs.contentPadding)}
}
img,
svg {
@@ -223,7 +292,6 @@ export class DxcCardComponent implements OnInit {
border-bottom-right-radius: 4px;
}
}
-
${this.applyTheme(inputs.linkHref, inputs.outlined)}
`;
}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.module.ts b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.module.ts
index 5abae8f04..076d72ee6 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.module.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.module.ts
@@ -3,10 +3,11 @@ import { MatCardModule } from "@angular/material/card";
import { DxcCardComponent } from "./dxc-card.component";
import { CommonModule } from "@angular/common";
import { BackgroundProviderModule } from "../background-provider/background-provider.module";
+import { DxcBoxModule } from '../dxc-box/dxc-box.module';
@NgModule({
declarations: [DxcCardComponent],
- imports: [CommonModule, MatCardModule, BackgroundProviderModule],
+ imports: [CommonModule, MatCardModule, DxcBoxModule, BackgroundProviderModule],
exports: [DxcCardComponent]
})
export class DxcCardModule {}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.types.ts b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.types.ts
new file mode 100644
index 000000000..e9a785ae2
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-card/dxc-card.types.ts
@@ -0,0 +1,28 @@
+export type Space =
+ | "xxsmall"
+ | "xsmall"
+ | "small"
+ | "medium"
+ | "large"
+ | "xlarge"
+ | "xxlarge";
+
+export type Spacing = {
+ top?: Space;
+ bottom?: Space;
+ left?: Space;
+ right?: Space;
+};
+
+export interface CardProperties {
+ imageSrc: string;
+ imageBgColor: string;
+ imagePadding: Space | Spacing;
+ imagePosition: "after" | "before";
+ contentPadding: Space | Spacing;
+ linkHref: string;
+ imageCover: boolean;
+ margin: Space | Spacing;
+ tabIndexValue: number;
+ outlined: boolean;
+}
\ No newline at end of file
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.html
index 611060c2b..54398c26f 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.html
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.html
@@ -1,12 +1,16 @@
-
+
-
-
\ No newline at end of file
+
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.ts
index bc252a228..4e618f768 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tab/dxc-tab.component.ts
@@ -17,13 +17,43 @@ import { TabService } from "../services/tab.service";
templateUrl: "./dxc-tab.component.html",
})
export class DxcTabComponent implements OnChanges {
- //Default values
+ /**
+ * Text to be placed within the tab.
+ */
@Input() label: string;
+
+ /**
+ * @deprecated The path of an icon to be placed within the tab.
+ */
@Input() iconSrc: string;
+
+ /**
+ * Whether the tab is disabled or not.
+ */
@Input() disabled: boolean = false;
+
+ /**
+ * It can have boolean type or number type.
+ * If the value is 'true', an empty badge will appear. If it is 'false',
+ * no badge will appear.
+ * If a number is put it will be shown as the label of the notification
+ * in the tab, taking into account that if that number is greater than 99,
+ * it will appear as '+99' in the badge.
+ */
+ @Input() notificationNumber: boolean | number;
+
+ /**
+ * This event will emit when the user clicks on a tab. The index
+ * of the clicked tab will be passed as a parameter.
+ */
+ @Output() onTabClick: EventEmitter
= new EventEmitter();
+
+ /**
+ * This event will emit when the user is on hover on a tab.
+ */
+ @Output() onTabHover: EventEmitter = new EventEmitter();
+
@Input() id: number;
- @Output() onTabClick = new EventEmitter();
- @Output() onTabHover = new EventEmitter();
showDotIndicator: boolean = false;
labelClass: string;
@@ -37,6 +67,8 @@ export class DxcTabComponent implements OnChanges {
iconPosition: string;
+ notificationValue: any;
+
constructor(private cdRef: ChangeDetectorRef, private service: TabService) {
this.service.iconPosition.subscribe((value) => {
if (value) {
@@ -46,6 +78,13 @@ export class DxcTabComponent implements OnChanges {
});
}
+ public ngOnInit(): void {
+ this.notificationValue =
+ typeof this.notificationNumber === "boolean"
+ ? ""
+ : this.notificationNumber;
+ }
+
public ngOnChanges(): void {
this.getLabelClass();
if (this.matTab) {
@@ -60,12 +99,16 @@ export class DxcTabComponent implements OnChanges {
this.tabIcon = true;
}
this.getLabelClass();
- this.cdRef.detectChanges();
this.matTab.disabled = this.disabled;
+ this.cdRef.detectChanges();
}
public onClickHandler(): void {
- this.onTabClick.emit(this.id);
+ if (!this.matTab.disabled) {
+ this.onTabClick.emit(this.id);
+ } else {
+ this.matTab.isActive = false;
+ }
}
public onHoverHandler(): void {
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.html
index b49e34cf1..cd8d880ec 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.html
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.html
@@ -1,7 +1,7 @@
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.scss b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.scss
index 86f87529e..a37095a27 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.scss
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.scss
@@ -2,7 +2,7 @@
.filled-tabs {
::ng-deep {
.mat-ink-bar {
- background-color: var(--tabs-selectedUnderlinedColor);
+ background-color: var(--tabs-selectedUnderlineColor);
}
}
}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.spec.ts b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.spec.ts
index ea6623756..135e01dfc 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.spec.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.spec.ts
@@ -1,71 +1,146 @@
import { render, fireEvent } from "@testing-library/angular";
import { screen } from "@testing-library/dom";
-import { DxcTabsComponent } from "./dxc-tabs.component";
-import { DxcTabsModule } from './dxc-tabs.module';
+import { DxcTabsModule } from "./dxc-tabs.module";
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting,
+} from "@angular/platform-browser-dynamic/testing";
+import { TestBed } from "@angular/core/testing";
+
+TestBed.initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
describe("DxcTabs tests", () => {
test("should render dxc-tabs", async () => {
- const tabs = await render(DxcTabsComponent, {
- template: `
-
-
-
- `,
- componentProperties: { },
- imports: [DxcTabsModule],
- excludeComponentDeclaration: true
- });
+ const tabs = await render(
+ `
+
+
+
+ `,
+ {
+ componentProperties: {},
+ imports: [DxcTabsModule],
+ excludeComponentDeclaration: true,
+ }
+ );
tabs.detectChanges();
expect(tabs.getByText("Tab1")).toBeTruthy();
expect(tabs.getByText("Tab2")).toBeTruthy();
expect(tabs.getByText("Tab3")).toBeTruthy();
- });
+ });
+
+ test("should render dxc-tabs with default value", async () => {
+ const clickFunction = jest.fn();
+ const tabs = await render(
+ `
+
+
+
+ `,
+ {
+ componentProperties: { clickFunction },
+ imports: [DxcTabsModule],
+ excludeComponentDeclaration: true,
+ }
+ );
+ tabs.detectChanges();
+ const arrTabs = screen.getAllByRole("tab");
+ expect(arrTabs[0].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[1].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[2].getAttribute("aria-selected")).toBe("true");
+ expect(tabs.getByText("Tab1")).toBeTruthy();
+ expect(tabs.getByText("Tab2")).toBeTruthy();
+ expect(tabs.getByText("Tab3")).toBeTruthy();
+
+ const tab1 = screen.getByText("Tab1");
+ fireEvent.click(tab1);
+ tabs.detectChanges();
+ expect(clickFunction).toHaveBeenCalledWith(0);
+ expect(arrTabs[0].getAttribute("aria-selected")).toBe("true");
+ expect(arrTabs[1].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[2].getAttribute("aria-selected")).toBe("false");
+ });
- test("should render uncontrolled tabs", async () => {
- const clickFunction = jest.fn();
- const tabs = await render(DxcTabsComponent, {
- template: `
-
-
-
- `,
+ test("should render dxc-badge", async () => {
+ const tabs = await render(
+ `
+
+
+
+ `,
+ {
+ componentProperties: {},
+ imports: [DxcTabsModule],
+ excludeComponentDeclaration: true,
+ }
+ );
+ tabs.detectChanges();
+ expect(tabs.getByText("90")).toBeTruthy();
+ expect(tabs.getByText("+99")).toBeTruthy();
+ });
+
+ test("should render uncontrolled tabs", async () => {
+ const clickFunction = jest.fn();
+ const tabs = await render(
+ `
+
+
+
+ `,
+ {
componentProperties: { clickFunction },
imports: [DxcTabsModule],
- excludeComponentDeclaration: true
- });
- tabs.detectChanges();
- const tab1 = screen.getByText("Tab1");
- const tab2 = screen.getByText("Tab2");
- fireEvent.click(tab2);
- tabs.detectChanges();
- expect(clickFunction).toHaveBeenCalledWith(1);
- tabs.detectChanges();
- fireEvent.click(tab1);
- tabs.detectChanges();
- expect(clickFunction).toHaveBeenCalledWith(0);
- });
+ excludeComponentDeclaration: true,
+ }
+ );
+ tabs.detectChanges();
+ const tab1 = screen.getByText("Tab1");
+ const tab2 = screen.getByText("Tab2");
+ fireEvent.click(tab2);
+ tabs.detectChanges();
+ expect(clickFunction).toHaveBeenCalledWith(1);
+ tabs.detectChanges();
+ fireEvent.click(tab1);
+ tabs.detectChanges();
+ expect(clickFunction).toHaveBeenCalledWith(0);
+ });
- test("should render controlled tabs", async () => {
- const clickFunction = jest.fn();
- const tabs = await render(DxcTabsComponent, {
- template: `
-
-
-
- `,
+ test("should render controlled tabs", async () => {
+ const clickFunction = jest.fn();
+ const tabs = await render(
+ `
+
+
+
+ `,
+ {
componentProperties: { clickFunction },
imports: [DxcTabsModule],
- excludeComponentDeclaration: true
- });
- tabs.detectChanges();
- const tab2 = screen.getByText("Tab2");
- const tab3 = screen.getByText("Tab3");
- fireEvent.click(tab2);
- tabs.detectChanges();
- expect(clickFunction).toHaveBeenCalledWith(1);
- tabs.detectChanges();
- fireEvent.click(tab3);
- tabs.detectChanges();
- expect(clickFunction).toHaveBeenCalledWith(2);
- });
-});
\ No newline at end of file
+ excludeComponentDeclaration: true,
+ }
+ );
+ tabs.detectChanges();
+ const arrTabs = screen.getAllByRole("tab");
+ expect(arrTabs[0].getAttribute("aria-selected")).toBe("true");
+ expect(arrTabs[1].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[2].getAttribute("aria-selected")).toBe("false");
+ const tab2 = screen.getByText("Tab2");
+ const tab3 = screen.getByText("Tab3");
+ fireEvent.click(tab2);
+ tabs.detectChanges();
+ expect(clickFunction).toHaveBeenCalledWith(1);
+ expect(arrTabs[0].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[1].getAttribute("aria-selected")).toBe("true");
+ expect(arrTabs[2].getAttribute("aria-selected")).toBe("false");
+ tabs.detectChanges();
+ fireEvent.click(tab3);
+ tabs.detectChanges();
+ expect(clickFunction).toHaveBeenCalledWith(2);
+ expect(arrTabs[0].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[1].getAttribute("aria-selected")).toBe("false");
+ expect(arrTabs[2].getAttribute("aria-selected")).toBe("true");
+ });
+});
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.ts
index a563ae318..b042676ce 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.component.ts
@@ -21,6 +21,7 @@ import {
MAT_RIPPLE_GLOBAL_OPTIONS,
RippleGlobalOptions,
} from "@angular/material/core";
+import { TabsProperties, Spacing, Space } from "./dxc-tabs.types";
const globalRippleConfig: RippleGlobalOptions = {
animation: {
@@ -28,6 +29,7 @@ const globalRippleConfig: RippleGlobalOptions = {
exitDuration: 0,
},
};
+
@Component({
selector: "dxc-tabs",
templateUrl: "./dxc-tabs.component.html",
@@ -39,13 +41,9 @@ const globalRippleConfig: RippleGlobalOptions = {
],
})
export class DxcTabsComponent implements OnChanges {
- @HostBinding("class") className;
- @HostBinding("class.label-icons") allTabWithLabelAndIcon: boolean = false;
-
- //Default values
- @Input() margin: any;
- @Input() iconPosition: string;
-
+ /**
+ * The index of the active tab.
+ */
@Input()
get activeTabIndex(): number {
return this._activeTabIndex;
@@ -53,7 +51,36 @@ export class DxcTabsComponent implements OnChanges {
set activeTabIndex(value: number) {
this._activeTabIndex = coerceNumberProperty(value);
}
- private _activeTabIndex;
+ private _activeTabIndex = 0;
+
+ /**
+ * Initially active tab, only when it is uncontrolled
+ */
+ @Input()
+ get defaultActiveTabIndex(): number {
+ return this._defaultActiveTabIndex;
+ }
+ set defaultActiveTabIndex(value: number) {
+ this._defaultActiveTabIndex = coerceNumberProperty(value);
+ }
+ private _defaultActiveTabIndex = 0;
+
+ /**
+ * Position of icons in tabs.
+ */
+ @Input() iconPosition: "top" | "left" = "left";
+
+ /**
+ * Size of the margin to be applied to the component
+ * ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge').
+ * You can pass an object with 'top', 'bottom', 'left' and 'right' properties
+ * in order to specify different margin sizes.
+ */
+ @Input() margin: Space | Spacing;
+
+ @HostBinding("class") className;
+ @HostBinding("class.label-icons") allTabWithLabelAndIcon: boolean = false;
+
renderedActiveTabIndex: number;
@ViewChild(MatRipple) ripple: MatRipple;
@@ -64,9 +91,10 @@ export class DxcTabsComponent implements OnChanges {
@ContentChildren(DxcTabComponent)
protected tabs: QueryList;
- defaultInputs = new BehaviorSubject({
+ defaultInputs = new BehaviorSubject({
+ activeTabIndex: 0,
+ iconPosition: "left",
margin: null,
- iconPosition: null,
});
constructor(
@@ -82,7 +110,6 @@ export class DxcTabsComponent implements OnChanges {
if (this.tabs && this.tabs.length > 0) {
this.generateTabs();
}
-
const inputs = Object.keys(changes).reduce((result, item) => {
result[item] = changes[item].currentValue;
return result;
@@ -93,6 +120,9 @@ export class DxcTabsComponent implements OnChanges {
ngOnInit() {
this.service.iconPosition.next(this.iconPosition || "left");
+ this.activeTabIndex = this.defaultActiveTabIndex
+ ? this.defaultActiveTabIndex
+ : this.activeTabIndex;
this.renderedActiveTabIndex = this.activeTabIndex;
this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
}
@@ -114,6 +144,29 @@ export class DxcTabsComponent implements OnChanges {
this.cdRef.detectChanges();
this.setEventListeners();
this.cdRef.detectChanges();
+
+ this.tabs.changes.subscribe((value) => {
+ const matTabsFromQueryList = value.map((tab, index) => {
+ if (tab.label && tab.iconSrc) {
+ this.allTabWithLabelAndIcon = true;
+ }
+ tab.id = index;
+ return tab.matTab;
+ });
+ const list = new QueryList();
+ list.reset([matTabsFromQueryList]);
+ this.tabGroup._tabs = list;
+ this.setActiveTab();
+ this.cdRef.detectChanges();
+ });
+ }
+
+ private hasLabelAndIcon() {
+ return (
+ this.tabs &&
+ this.tabs.filter((tab) => tab.label !== null && tab.iconSrc !== null)
+ .length > 0
+ );
}
private generateTabs() {
@@ -131,7 +184,8 @@ export class DxcTabsComponent implements OnChanges {
}
setEventListeners() {
- let tabLabels = this._element.nativeElement.getElementsByClassName("mat-tab-label");
+ let tabLabels =
+ this._element.nativeElement.getElementsByClassName("mat-tab-label");
if (tabLabels?.length > 0) {
this.tabs.map((tab, index) => {
tabLabels[index].addEventListener("click", function () {
@@ -145,9 +199,8 @@ export class DxcTabsComponent implements OnChanges {
}
insertUnderline() {
- let tabList = this._element.nativeElement.getElementsByClassName(
- "mat-tab-list"
- )[0];
+ let tabList =
+ this._element.nativeElement.getElementsByClassName("mat-tab-list")[0];
tabList.insertAdjacentHTML("beforeend", '');
}
@@ -157,24 +210,33 @@ export class DxcTabsComponent implements OnChanges {
box-shadow: none;
}
.mat-tab-list .underline {
- height: 1px;
+ height: var(--tabs-dividerThickness);
width: 100%;
- background-color: var(--tabs-divider);
+ background-color: var(--tabs-dividerColor);
}
.mat-tab-group {
+ position: relative;
+ display: flex;
+ flex-direction: column;
${this.utils.getMargins(inputs.margin)}
.mat-tab-header {
background-color: white;
}
+ .mat-ink-bar {
+ background-color: var(--tabs-selectedUnderlineColor);
+ height: var(--tabs-selectedUnderlineThickness);
+ }
}
.mat-tab-list .mat-tab-label {
- height: auto !important;
- /* max-width: 360px; */
+ height: ${this.getTabHeight()} !important;
padding-right: 16px;
padding-left: 16px;
+ min-width: 90px;
+ max-width: 360px;
+ text-transform: var(--tabs-fontTextTransform) !important;
opacity: 1 !important;
- /* min-width: 90px; */
- background: var(--tabs-backgroundColor) 0% 0% no-repeat;
+ color: var(--tabs-unselectedFontColor);
+ background: var(--tabs-unselectedBackgroundColor) 0% 0% no-repeat;
.dxc-tab-label span:not(.show-dot) {
opacity: 1;
white-space: normal;
@@ -182,8 +244,10 @@ export class DxcTabsComponent implements OnChanges {
.dxc-tab-label span {
color: var(--tabs-fontColor);
opacity: 1;
- font: normal normal 600 16px/22px var(--fontFamily);
- ;
+ font-family: var(--tabs-fontFamily);
+ font-size: var(--tabs-fontSize);
+ font-style: var(--tabs-fontStyle);
+ font-weight: var(--tabs-fontWeight);
}
&.cdk-focused {
outline: -webkit-focus-ring-color auto 1px;
@@ -194,6 +258,7 @@ export class DxcTabsComponent implements OnChanges {
fill: var(--tabs-fontColor);
}
.mat-ripple-element {
+ font-weight: var(--tabs-pressedFontWeight) !important;
background-color: var(--tabs-pressedBackgroundColor);
}
.mat-tab-label-content {
@@ -201,6 +266,13 @@ export class DxcTabsComponent implements OnChanges {
display: inline-grid;
text-align: -webkit-center;
z-index: 1;
+
+ dxc-badge {
+ position: absolute;
+ top: 12px;
+ right: 4px;
+ }
+
img,
svg {
width: 22px;
@@ -231,7 +303,6 @@ export class DxcTabsComponent implements OnChanges {
flex-direction: column;
align-items: center;
justify-content: center;
- /* min-width: 90px; */
}
.only-icon {
min-height: 64px;
@@ -240,15 +311,8 @@ export class DxcTabsComponent implements OnChanges {
display: grid;
}
}
- &.mat-tab-disabled {
- .dxc-tab-label span {
- color: var(--tabs-disabledFontColor) !important;
- }
- cursor: not-allowed;
- pointer-events: all !important;
- }
&.mat-tab-label-active {
- /* background-color: var(--tabs-backgroundColor); */
+ background-color: var(--tabs-selectedBackgroundColor);
opacity: 1 !important;
.dxc-tab-label span {
color: var(--tabs-selectedFontColor);
@@ -259,6 +323,18 @@ export class DxcTabsComponent implements OnChanges {
fill: var(--tabs-selectedIconColor);
}
}
+ &.mat-tab-disabled {
+ .dxc-tab-label span {
+ color: var(--tabs-disabledFontColor) !important;
+ font-style: var(--tabs-disabledFontStyle);
+ }
+ dxc-tab-icon {
+ fill: var(--tabs-disabledIconColor);
+ }
+ opacity: 0.5 !important;
+ cursor: not-allowed;
+ pointer-events: all !important;
+ }
}
&.label-icons {
.mat-tab-list .mat-tab-label {
@@ -267,4 +343,14 @@ export class DxcTabsComponent implements OnChanges {
}
`;
}
+
+ getTabHeight() {
+ return (
+ ((!this.hasLabelAndIcon ||
+ (this.hasLabelAndIcon &&
+ this.defaultInputs.value.iconPosition !== "top")) &&
+ "48px") ||
+ "72px"
+ );
+ }
}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.module.ts b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.module.ts
index 34b44c786..162fbc3d1 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.module.ts
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.module.ts
@@ -6,7 +6,9 @@ import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
import { DxcTabsComponent } from "./dxc-tabs.component";
import { DxcTabComponent } from "./dxc-tab/dxc-tab.component";
-import { DxcTabIconComponent } from './dxc-tab/dxc-tab-icon/dxc-tab-icon.component';
+import { DxcTabIconComponent } from "./dxc-tab/dxc-tab-icon/dxc-tab-icon.component";
+import { DxcBadgeComponent } from "../dxc-badge/dxc-badge.component";
+import { DxcBadgeModule } from "../dxc-badge/dxc-badge.module";
@NgModule({
declarations: [DxcTabsComponent, DxcTabComponent, DxcTabIconComponent],
@@ -15,9 +17,10 @@ import { DxcTabIconComponent } from './dxc-tab/dxc-tab-icon/dxc-tab-icon.compone
MatInputModule,
MatTabsModule,
MatFormFieldModule,
+ DxcBadgeModule,
FormsModule,
],
- exports: [DxcTabsComponent, DxcTabComponent,DxcTabIconComponent],
+ exports: [DxcTabsComponent, DxcTabComponent, DxcTabIconComponent],
entryComponents: [MatTab, MatTabGroup],
})
export class DxcTabsModule {}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.types.ts b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.types.ts
new file mode 100644
index 000000000..6c08ac528
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-tabs/dxc-tabs.types.ts
@@ -0,0 +1,22 @@
+export type Space =
+ | "xxsmall"
+ | "xsmall"
+ | "small"
+ | "medium"
+ | "large"
+ | "xlarge"
+ | "xxlarge";
+
+export type Spacing = {
+ top?: Space;
+ bottom?: Space;
+ left?: Space;
+ right?: Space;
+};
+
+export interface TabsProperties {
+ activeTabIndex?: number;
+ iconPosition?: "top" | "left";
+ margin?: Space | Spacing;
+ defaultActiveTabIndex?: number;
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-wizard/dxc-wizard-step/dxc-wizard-step.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-wizard/dxc-wizard-step/dxc-wizard-step.component.html
index f6fa413c8..493257cb1 100644
--- a/projects/dxc-ngx-cdk/src/lib/dxc-wizard/dxc-wizard-step/dxc-wizard-step.component.html
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-wizard/dxc-wizard-step/dxc-wizard-step.component.html
@@ -10,6 +10,7 @@
[disabled]="disabled"
(click)="!disabled ? handleStepClick() : ''"
[tabindex]="disabled ? -1 : tabIndexValue"
+ [attr.aria-current]="isCurrent"
>