diff --git a/CHANGELOG.md b/CHANGELOG.md
index 625d2446..c377cdd7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 1.0.0-beta3 (19.04.2021)
+
+### New components:
+
+- [Alerts](https://mdbootstrap.com/docs/b5/angular/components/alerts/)
+- [Carousel](https://mdbootstrap.com/docs/b5/angular/components/carousel)
+- [Toasts](https://mdbootstrap.com/docs/b5/angular/components/toasts)
+
+### Bug fixes:
+
+- Datepicker - resolved problem with keyboard navigation when using `DownArrow` key,
+- Datepicker - resolved problem with selecting dates using `Enter/Space` keys in component with date filter,
+- Datepicker - added correct aria-labels to the previous/next buttons in the days view.
+
+---
+
## 1.0.0-beta2 (06.04.2021)
### New components:
diff --git a/README.txt b/README.txt
index 06e04be5..e4157518 100644
--- a/README.txt
+++ b/README.txt
@@ -1,6 +1,6 @@
MDB 5 Angular
-Version: FREE 1.0.0 Beta 2
+Version: FREE 1.0.0 Beta 3
Documentation:
https://mdbootstrap.com/docs/b5/angular/
diff --git a/package.json b/package.json
index f3f73763..7093d495 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mdb-angular-ui-kit-free",
- "version": "1.0.0-beta2",
+ "version": "1.0.0-beta3",
"scripts": {
"ng": "ng",
"start": "ng serve",
diff --git a/projects/mdb-angular-ui-kit/CHANGELOG.md b/projects/mdb-angular-ui-kit/CHANGELOG.md
index 625d2446..c377cdd7 100644
--- a/projects/mdb-angular-ui-kit/CHANGELOG.md
+++ b/projects/mdb-angular-ui-kit/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 1.0.0-beta3 (19.04.2021)
+
+### New components:
+
+- [Alerts](https://mdbootstrap.com/docs/b5/angular/components/alerts/)
+- [Carousel](https://mdbootstrap.com/docs/b5/angular/components/carousel)
+- [Toasts](https://mdbootstrap.com/docs/b5/angular/components/toasts)
+
+### Bug fixes:
+
+- Datepicker - resolved problem with keyboard navigation when using `DownArrow` key,
+- Datepicker - resolved problem with selecting dates using `Enter/Space` keys in component with date filter,
+- Datepicker - added correct aria-labels to the previous/next buttons in the days view.
+
+---
+
## 1.0.0-beta2 (06.04.2021)
### New components:
diff --git a/projects/mdb-angular-ui-kit/LICENSE b/projects/mdb-angular-ui-kit/LICENSE
new file mode 100644
index 00000000..64ef5206
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 MDBootstrap
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/projects/mdb-angular-ui-kit/License.pdf b/projects/mdb-angular-ui-kit/License.pdf
deleted file mode 100644
index 9db6ccc2..00000000
Binary files a/projects/mdb-angular-ui-kit/License.pdf and /dev/null differ
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_carousel.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_carousel.scss
index a61404d5..c398669f 100644
--- a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_carousel.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_carousel.scss
@@ -96,8 +96,11 @@
align-items: center; // 2. vertically center contents
justify-content: center; // 3. horizontally center contents
width: $carousel-control-width;
+ padding: 0;
color: $carousel-control-color;
text-align: center;
+ background: none;
+ border: 0;
opacity: $carousel-control-opacity;
@include transition($carousel-control-transition);
@@ -153,10 +156,10 @@
background-image: escape-svg($carousel-control-next-icon-bg);
}
-// Optional indicator pips
+// Optional indicator pips/controls
//
-// Add an ordered list with the following class and add a list item for each
-// slide your carousel holds.
+// Add a container (such as a list) with the following class and add an item (ideally a focusable control,
+// like a button) with data-bs-target for each slide your carousel holds.
.carousel-indicators {
position: absolute;
@@ -166,23 +169,26 @@
z-index: 2;
display: flex;
justify-content: center;
- padding-left: 0; // override
default
+ padding: 0;
// Use the .carousel-control's width as margin so we don't overlay those
margin-right: $carousel-control-width;
+ margin-bottom: 1rem;
margin-left: $carousel-control-width;
list-style: none;
- li {
+ button {
box-sizing: content-box;
flex: 0 1 auto;
width: $carousel-indicator-width;
height: $carousel-indicator-height;
+ padding: 0;
margin-right: $carousel-indicator-spacer;
margin-left: $carousel-indicator-spacer;
text-indent: -999px;
cursor: pointer;
background-color: $carousel-indicator-active-bg;
background-clip: padding-box;
+ border: 0;
// Use transparent borders to increase the hit area by 10px on top and bottom.
border-top: $carousel-indicator-hit-area-height solid transparent;
border-bottom: $carousel-indicator-hit-area-height solid transparent;
@@ -218,7 +224,7 @@
filter: $carousel-dark-control-icon-filter;
}
- .carousel-indicators li {
+ .carousel-indicators button {
background-color: $carousel-dark-indicator-active-bg;
}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss
new file mode 100644
index 00000000..47cc3357
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss
@@ -0,0 +1,18 @@
+// Alert
+
+.alert {
+ border: 0;
+}
+
+.alert-absolute {
+ position: absolute;
+}
+
+.alert-fixed {
+ position: fixed;
+ z-index: $zindex-alert;
+}
+
+.parent-alert-relative {
+ position: relative;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss
new file mode 100644
index 00000000..bc38b4cc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss
@@ -0,0 +1,25 @@
+mdb-carousel {
+ display: block;
+}
+
+.carousel-control-prev-icon {
+ &::after {
+ content: '\f053';
+ font-weight: $font-weight-bold;
+ font-family: 'Font Awesome 5 Pro', 'Font Awesome 5 Free';
+ font-size: 1.7rem;
+ }
+}
+.carousel-control-next-icon {
+ &::after {
+ content: '\f054';
+ font-weight: $font-weight-bold;
+ font-family: 'Font Awesome 5 Pro', 'Font Awesome 5 Free';
+ font-size: 1.7rem;
+ }
+}
+.carousel-indicators {
+ [data-mdb-target] {
+ @extend [data-bs-target] !optional;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss
new file mode 100644
index 00000000..767b7bfa
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss
@@ -0,0 +1,32 @@
+// Toasts
+
+.toast {
+ background-color: $toast-background-color;
+ border: 0;
+ box-shadow: $toast-box-shadow;
+
+ .btn-close {
+ width: 1.3em;
+ }
+}
+
+.toast-header {
+ background-color: $toast-header-background-color;
+}
+
+.parent-toast-relative {
+ position: relative;
+}
+
+.toast-absolute {
+ position: absolute;
+}
+
+.toast-fixed {
+ position: fixed;
+ z-index: $zindex-toast;
+}
+
+.toast:not(.showing):not(.show) {
+ opacity: 1;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss b/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss
index 8e7ff4df..36b5fcdb 100644
--- a/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss
@@ -80,6 +80,8 @@
@import './free/popover';
@import './free/dropdown';
@import './free/range';
+@import './free/alert';
+@import './free/toasts';
// MDB FORMS
@import './free/forms/form-check';
diff --git a/projects/mdb-angular-ui-kit/assets/scss/mdb.scss b/projects/mdb-angular-ui-kit/assets/scss/mdb.scss
index e45c03aa..647a9c8c 100644
--- a/projects/mdb-angular-ui-kit/assets/scss/mdb.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/mdb.scss
@@ -70,6 +70,7 @@
@import './free/nav';
@import './free/navbar';
@import './free/card';
+@import './free/carousel';
@import './free/breadcrumb';
@import './free/pagination';
@import './free/badge';
@@ -84,6 +85,8 @@
@import './free/validation';
@import './free/scrollspy';
@import './free/range';
+@import './free/alert';
+@import './free/toasts';
// MDB FORMS
@import './free/forms/form-check';
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts b/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts
new file mode 100644
index 00000000..49a0533b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts
@@ -0,0 +1,28 @@
+import { Component, ElementRef, HostBinding, Input, OnInit } from '@angular/core';
+
+@Component({
+ // tslint:disable-next-line: component-selector
+ selector: 'mdb-carousel-item',
+ template: ' ',
+})
+export class MdbCarouselItemComponent implements OnInit {
+ @Input() interval: number | null = null;
+
+ @HostBinding('class.carousel-item')
+ carouselItem = true;
+
+ @HostBinding('class.active') active = false;
+
+ @HostBinding('class.carousel-item-next') next = false;
+ @HostBinding('class.carousel-item-prev') prev = false;
+ @HostBinding('class.carousel-item-start') start = false;
+ @HostBinding('class.carousel-item-end') end = false;
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ constructor(private _elementRef: ElementRef) {}
+
+ ngOnInit(): void {}
+}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.component.html b/projects/mdb-angular-ui-kit/carousel/carousel.component.html
new file mode 100644
index 00000000..fbd79a1b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.component.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ Previous
+
+
+
+ Next
+
+
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.component.ts b/projects/mdb-angular-ui-kit/carousel/carousel.component.ts
new file mode 100644
index 00000000..2174ecf7
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.component.ts
@@ -0,0 +1,360 @@
+import {
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChildren,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Input,
+ OnDestroy,
+ Output,
+ QueryList,
+} from '@angular/core';
+import { fromEvent, Subject } from 'rxjs';
+import { take, takeUntil } from 'rxjs/operators';
+import { MdbCarouselItemComponent } from './carousel-item.component';
+
+export enum Direction {
+ UNKNOWN,
+ NEXT,
+ PREV,
+}
+
+@Component({
+ // tslint:disable-next-line: component-selector
+ selector: 'mdb-carousel',
+ templateUrl: './carousel.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MdbCarouselComponent implements AfterViewInit, OnDestroy {
+ @ContentChildren(MdbCarouselItemComponent) _items: QueryList;
+ get items(): MdbCarouselItemComponent[] {
+ return this._items && this._items.toArray();
+ }
+
+ @Input() animation: 'slide' | 'fade' = 'slide';
+ @Input() controls = false;
+ @Input() dark = false;
+ @Input() indicators = false;
+ @Input() ride = true;
+
+ @Input()
+ get interval(): number {
+ return this._interval;
+ }
+ set interval(value: number) {
+ this._interval = value;
+
+ if (this.items) {
+ this._restartInterval();
+ }
+ }
+ private _interval = 5000;
+
+ @Input() keyboard = true;
+ @Input() pause = true;
+ @Input() wrap = true;
+
+ @Output() slide: EventEmitter = new EventEmitter();
+ @Output() slideChange: EventEmitter = new EventEmitter();
+
+ get activeSlide(): number {
+ return this._activeSlide;
+ }
+
+ set activeSlide(index: number) {
+ if (this.items.length && this._activeSlide !== index) {
+ this._activeSlide = index;
+ this._restartInterval();
+ }
+ }
+ private _activeSlide = 0;
+
+ private _lastInterval: any;
+ private _isPlaying = false;
+ private _isSliding = false;
+
+ private readonly _destroy$: Subject = new Subject();
+
+ @HostListener('mouseenter')
+ onMouseEnter(): void {
+ if (this.pause && this._isPlaying) {
+ this.stop();
+ }
+ }
+
+ @HostListener('mouseleave')
+ onMouseLeave(): void {
+ if (this.pause && !this._isPlaying) {
+ this.play();
+ }
+ }
+
+ constructor(private _elementRef: ElementRef, private _cdRef: ChangeDetectorRef) {}
+
+ ngAfterViewInit(): void {
+ Promise.resolve().then(() => {
+ this._setActiveSlide(this._activeSlide);
+
+ if (this.interval > 0 && this.ride) {
+ this.play();
+ }
+ });
+
+ if (this.keyboard) {
+ fromEvent(this._elementRef.nativeElement, 'keydown')
+ .pipe(takeUntil(this._destroy$))
+ // tslint:disable-next-line: deprecation
+ .subscribe((event: KeyboardEvent) => {
+ if (event.key === 'ArrowRight') {
+ this.next();
+ } else if (event.key === 'ArrowLeft') {
+ this.prev();
+ }
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _setActiveSlide(index: number): void {
+ const currentSlide = this.items[this._activeSlide];
+ currentSlide.active = false;
+
+ const newSlide = this.items[index];
+ newSlide.active = true;
+ this._activeSlide = index;
+ }
+
+ private _restartInterval(): void {
+ this._resetInterval();
+ const activeElement = this.items[this.activeSlide];
+ const interval = activeElement.interval ? activeElement.interval : this.interval;
+
+ if (!isNaN(interval) && interval > 0) {
+ this._lastInterval = setInterval(() => {
+ const nInterval = +interval;
+ if (this._isPlaying && !isNaN(nInterval) && nInterval > 0) {
+ this.next();
+ } else {
+ this.stop();
+ }
+ }, interval);
+ }
+ }
+
+ private _resetInterval(): void {
+ if (this._lastInterval) {
+ clearInterval(this._lastInterval);
+ this._lastInterval = null;
+ }
+ }
+
+ play(): void {
+ if (!this._isPlaying) {
+ this._isPlaying = true;
+ this._restartInterval();
+ }
+ }
+
+ stop(): void {
+ if (this._isPlaying) {
+ this._isPlaying = false;
+ this._resetInterval();
+ }
+ }
+
+ to(index: number): void {
+ if (index > this.items.length - 1 || index < 0) {
+ return;
+ }
+
+ if (this.activeSlide === index) {
+ this.stop();
+ this.play();
+ return;
+ }
+
+ const direction = index > this.activeSlide ? Direction.NEXT : Direction.PREV;
+
+ this._animateSlides(direction, this.activeSlide, index);
+ this.activeSlide = index;
+
+ this._cdRef.markForCheck();
+ }
+
+ next(): void {
+ if (!this._isSliding) {
+ this._slide(Direction.NEXT);
+ }
+
+ this._cdRef.markForCheck();
+ }
+
+ prev(): void {
+ if (!this._isSliding) {
+ this._slide(Direction.PREV);
+ }
+
+ this._cdRef.markForCheck();
+ }
+
+ private _slide(direction: Direction): void {
+ const isFirst = this._activeSlide === 0;
+ const isLast = this._activeSlide === this.items.length - 1;
+
+ if (!this.wrap) {
+ if ((direction === Direction.NEXT && isLast) || (direction === Direction.PREV && isFirst)) {
+ return;
+ }
+ }
+
+ const newSlideIndex = this._getNewSlideIndex(direction);
+
+ this._animateSlides(direction, this.activeSlide, newSlideIndex);
+ this.activeSlide = newSlideIndex;
+
+ this.slide.emit();
+ }
+
+ private _animateSlides(direction: Direction, currentIndex: number, nextIndex: number): void {
+ const currentItem = this.items[currentIndex];
+ const nextItem = this.items[nextIndex];
+ const currentEl = currentItem.host;
+ const nextEl = nextItem.host;
+
+ this._isSliding = true;
+
+ if (this._isPlaying) {
+ this.stop();
+ }
+
+ if (direction === Direction.NEXT) {
+ nextItem.next = true;
+
+ setTimeout(() => {
+ this._reflow(nextEl);
+ currentItem.start = true;
+ nextItem.start = true;
+ }, 0);
+
+ const transitionDuration = 600;
+
+ fromEvent(currentEl, 'transitionend')
+ .pipe(take(1))
+ // tslint:disable-next-line: deprecation
+ .subscribe(() => {
+ nextItem.next = false;
+ nextItem.start = false;
+ nextItem.active = true;
+
+ currentItem.active = false;
+ currentItem.start = false;
+ currentItem.next = false;
+
+ this.slideChange.emit();
+ this._isSliding = false;
+ });
+
+ this._emulateTransitionEnd(currentEl, transitionDuration);
+ } else if (direction === Direction.PREV) {
+ nextItem.prev = true;
+
+ setTimeout(() => {
+ this._reflow(nextEl);
+ currentItem.end = true;
+ nextItem.end = true;
+ }, 0);
+
+ const transitionDuration = 600;
+
+ fromEvent(currentEl, 'transitionend')
+ .pipe(take(1))
+ // tslint:disable-next-line: deprecation
+ .subscribe(() => {
+ nextItem.prev = false;
+ nextItem.end = false;
+ nextItem.active = true;
+
+ currentItem.active = false;
+ currentItem.end = false;
+ currentItem.prev = false;
+
+ this.slideChange.emit();
+ this._isSliding = false;
+ });
+
+ this._emulateTransitionEnd(currentEl, transitionDuration);
+ }
+
+ if (!this._isPlaying && this.interval > 0) {
+ this.play();
+ }
+ }
+
+ private _reflow(element: HTMLElement): number {
+ return element.offsetHeight;
+ }
+
+ private _emulateTransitionEnd(element: HTMLElement, duration: number): void {
+ let eventEmitted = false;
+ const durationPadding = 5;
+ const emulatedDuration = duration + durationPadding;
+
+ fromEvent(element, 'transitionend')
+ .pipe(take(1))
+ // tslint:disable-next-line: deprecation
+ .subscribe(() => {
+ eventEmitted = true;
+ });
+
+ setTimeout(() => {
+ if (!eventEmitted) {
+ element.dispatchEvent(new Event('transitionend'));
+ }
+ }, emulatedDuration);
+ }
+
+ private _getNewSlideIndex(direction: Direction): number {
+ let newSlideIndex: number;
+
+ if (direction === Direction.NEXT) {
+ newSlideIndex = this._getNextSlideIndex();
+ }
+
+ if (direction === Direction.PREV) {
+ newSlideIndex = this._getPrevSlideIndex();
+ }
+
+ return newSlideIndex;
+ }
+
+ private _getNextSlideIndex(): number {
+ const isLast = this._activeSlide === this.items.length - 1;
+
+ if (!isLast) {
+ return this._activeSlide + 1;
+ } else if (this.wrap && isLast) {
+ return 0;
+ } else {
+ return this._activeSlide;
+ }
+ }
+
+ private _getPrevSlideIndex(): number {
+ const isFirst = this._activeSlide === 0;
+
+ if (!isFirst) {
+ return this._activeSlide - 1;
+ } else if (this.wrap && isFirst) {
+ return this.items.length - 1;
+ } else {
+ return this._activeSlide;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.module.ts b/projects/mdb-angular-ui-kit/carousel/carousel.module.ts
new file mode 100644
index 00000000..67a70093
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { MdbCarouselComponent } from './carousel.component';
+import { MdbCarouselItemComponent } from './carousel-item.component';
+
+@NgModule({
+ declarations: [MdbCarouselComponent, MdbCarouselItemComponent],
+ exports: [MdbCarouselComponent, MdbCarouselItemComponent],
+ imports: [CommonModule],
+})
+export class MdbCarouselModule {}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts b/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts
new file mode 100644
index 00000000..561ba956
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts
@@ -0,0 +1,216 @@
+import { Component, ViewChild } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { MdbCarouselComponent } from './carousel.component';
+import { MdbCarouselModule } from './carousel.module';
+
+const carouselTemplate = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+@Component({
+ template: carouselTemplate,
+})
+export class CarouselTestComponent {
+ @ViewChild(MdbCarouselComponent, { static: true }) carousel: MdbCarouselComponent;
+ controls = false;
+ indicators = false;
+ wrap = true;
+ dark = false;
+ animation = 'slide';
+}
+
+describe('MDB Carousel', () => {
+ let fixture: ComponentFixture;
+ let component: CarouselTestComponent;
+ let carousel: MdbCarouselComponent;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [CarouselTestComponent],
+ imports: [MdbCarouselModule],
+ });
+
+ fixture = TestBed.createComponent(CarouselTestComponent);
+ component = fixture.componentInstance;
+ carousel = component.carousel;
+
+ fixture.detectChanges();
+ });
+
+ it('should set first slide as active by default', fakeAsync(() => {
+ flush();
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ expect(items[0].classList.contains('active')).toBe(true);
+ }));
+
+ it('should show indicators if indicators input is set to true', () => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators');
+ expect(indicators).toBeDefined();
+ });
+
+ it('should show controls if controls input is set to true', () => {
+ component.controls = true;
+ fixture.detectChanges();
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(prevArrow).toBeDefined();
+ expect(nextArrow).toBeDefined();
+ });
+
+ it('should add carousel-fade class if animation type is set to fade', () => {
+ component.animation = 'fade';
+ fixture.detectChanges();
+ const carouselEl = fixture.nativeElement.querySelector('.carousel');
+ expect(carouselEl.classList.contains('carousel-fade')).toBe(true);
+ });
+
+ it('should add carousel-dark class if dark input is set to true', () => {
+ component.dark = true;
+ fixture.detectChanges();
+ const carouselEl = fixture.nativeElement.querySelector('.carousel');
+ expect(carouselEl.classList.contains('carousel-dark')).toBe(true);
+ });
+
+ it('should set corresponding indicator as active', fakeAsync(() => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators > button');
+ expect(indicators[0].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change active slide on indicator click', fakeAsync(() => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators > button');
+ expect(indicators[0].classList.contains('active')).toBe(true);
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ indicators[1].click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(indicators[1].classList.contains('active')).toBe(true);
+ expect(items[1].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change slide on previous arrow click', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change slide on next arrow click', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+ }));
+
+ it('should not go to previous slide if first slide is active and wrap option is disabled', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = false;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[0].classList.contains('active')).toBe(true);
+ expect(items[2].classList.contains('active')).toBe(false);
+ }));
+
+ it('should not go to next slide if last slide is active and wrap option is disabled', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = false;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+ expect(items[0].classList.contains('active')).toBe(false);
+ }));
+});
diff --git a/projects/mdb-angular-ui-kit/carousel/index.ts b/projects/mdb-angular-ui-kit/carousel/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/carousel/public_api.ts b/projects/mdb-angular-ui-kit/carousel/public_api.ts
new file mode 100644
index 00000000..0c661600
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/public_api.ts
@@ -0,0 +1,3 @@
+export { MdbCarouselComponent } from './carousel.component';
+export { MdbCarouselItemComponent } from './carousel-item.component';
+export { MdbCarouselModule } from './carousel.module';
diff --git a/projects/mdb-angular-ui-kit/index.ts b/projects/mdb-angular-ui-kit/index.ts
index d832aecd..34f92c9e 100644
--- a/projects/mdb-angular-ui-kit/index.ts
+++ b/projects/mdb-angular-ui-kit/index.ts
@@ -15,6 +15,7 @@ import { MdbValidationModule } from './validation/validation.module';
import { MdbScrollspyModule } from './scrollspy/scrollspy.module';
import { MdbRangeModule } from './range/range.module';
import { MdbTabsModule } from './tabs/tabs.module';
+import { MdbCarouselModule } from './carousel/carousel.module';
export { MdbCollapseDirective, MdbCollapseModule } from './collapse/index';
export {
@@ -72,6 +73,11 @@ export {
MdbTabsComponent,
MdbTabsModule,
} from './tabs/index';
+export {
+ MdbCarouselComponent,
+ MdbCarouselItemComponent,
+ MdbCarouselModule,
+} from './carousel/index';
const MDB_MODULES = [
MdbCollapseModule,
@@ -87,6 +93,7 @@ const MDB_MODULES = [
MdbScrollspyModule,
MdbRangeModule,
MdbTabsModule,
+ MdbCarouselModule,
];
@NgModule({
diff --git a/projects/mdb-angular-ui-kit/package.json b/projects/mdb-angular-ui-kit/package.json
index cad94330..b2642a78 100644
--- a/projects/mdb-angular-ui-kit/package.json
+++ b/projects/mdb-angular-ui-kit/package.json
@@ -3,7 +3,7 @@
"repository": "https://github.com/mdbootstrap/mdb-angular-ui-kit",
"author": "MDBootstrap",
"license": "MIT",
- "version": "1.0.0-beta2",
+ "version": "1.0.0-beta3",
"peerDependencies": {
"@angular/common": "^11.0.0",
"@angular/core": "^11.0.0",
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts b/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts
index 79e0e5cb..1f92a0fe 100644
--- a/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts
@@ -25,7 +25,7 @@ export class TabsTestComponent {
@ViewChildren(MdbTabComponent) tabComponents: QueryList;
}
-describe('NbTabsetComponent', () => {
+describe('MDB Tabs', () => {
let fixture: ComponentFixture;
let component: TabsTestComponent;
let tabsComponent: MdbTabsComponent;