diff --git a/demo/src/app/components/carousel/demos/carousel-demo.component.html b/demo/src/app/components/carousel/demos/carousel-demo.component.html
index ea71e9ce52..4baedd97be 100644
--- a/demo/src/app/components/carousel/demos/carousel-demo.component.html
+++ b/demo/src/app/components/carousel/demos/carousel-demo.component.html
@@ -1,6 +1,6 @@
-
+
@@ -19,10 +19,14 @@ Slide {{index}}
+
+ git
diff --git a/demo/src/app/components/carousel/demos/carousel-demo.component.ts b/demo/src/app/components/carousel/demos/carousel-demo.component.ts
index 648209b7d3..ad933d0444 100644
--- a/demo/src/app/components/carousel/demos/carousel-demo.component.ts
+++ b/demo/src/app/components/carousel/demos/carousel-demo.component.ts
@@ -8,7 +8,7 @@ export class CarouselDemoComponent {
public myInterval:number = 5000;
public noWrapSlides:boolean = false;
public slides:any[] = [];
- public currentSlideIndex: number;
+ public activeSlideIndex: number;
public constructor() {
for (let i = 0; i < 4; i++) {
@@ -25,12 +25,12 @@ export class CarouselDemoComponent {
});
}
- public activeSlideChanged(index: number): void {
- this.currentSlideIndex = index;
+ public selectSlide(index: number): void {
+ this.activeSlideIndex = index;
}
public removeSlide(index?: number):void {
- const toRemove = index ? index : this.currentSlideIndex;
+ const toRemove = index ? index : this.activeSlideIndex;
this.slides.splice(toRemove, 1);
}
}
diff --git a/src/carousel/carousel.component.ts b/src/carousel/carousel.component.ts
index f9a92b9a1a..caadb11a3d 100644
--- a/src/carousel/carousel.component.ts
+++ b/src/carousel/carousel.component.ts
@@ -34,11 +34,11 @@ export enum Direction {UNKNOWN, NEXT, PREV}
- 1">
+ 1">
Previous
- 1">
+ 1">
Next
@@ -47,34 +47,50 @@ export enum Direction {UNKNOWN, NEXT, PREV}
})
export class CarouselComponent implements OnDestroy {
/** if `true` carousel will not cycle continuously and will have hard stops (prevent looping) */
- @Input() public noWrap:boolean;
+ @Input() public noWrap: boolean;
+
/** if `true` will disable pausing on carousel mouse hover */
- @Input() public noPause:boolean;
+ @Input() public noPause: boolean;
+
+ protected _currentActiveSlide: number;
+
+ /** Will be emitted when active slide has been changed. Part of two-way-bindable [(activeSlide)] property */
+ @Output() public activeSlideChange: EventEmitter = new EventEmitter(false);
+
+ /** Index of currently displayed slide(started for 0) */
+ @Input()
+ public set activeSlide(index: number) {
+ if (this._slides.length && index !== this._currentActiveSlide) {
+ this._select(index);
+ }
+ }
+ public get activeSlide(): number {
+ return this._currentActiveSlide;
+ }
+
+ protected _interval: number;
/**
* Amount of time in milliseconds to delay between automatically
* cycling an item. If false, carousel will not automatically cycle
*/
@Input()
- public get interval():number {
+ public get interval(): number {
return this._interval;
}
- public set interval(value:number) {
+ public set interval(value: number) {
this._interval = value;
this.restartTimer();
}
- @Output() public activeSlideChanged: EventEmitter = new EventEmitter(false);
-
protected _slides: LinkedList = new LinkedList();
public get slides(): SlideComponent[] {
return this._slides.toArray();
}
- protected currentInterval:any;
- protected isPlaying:boolean;
- protected destroyed:boolean = false;
- protected _interval:number;
+ protected currentInterval: any;
+ protected isPlaying: boolean;
+ protected destroyed: boolean = false;
public get isBs4():boolean {
return !isBs3();
@@ -84,123 +100,176 @@ export class CarouselComponent implements OnDestroy {
Object.assign(this, config);
}
- public ngOnDestroy():void {
+ public ngOnDestroy(): void {
this.destroyed = true;
}
+ /**
+ * Adds new slide. If this slide is first in collection - set it as active and starts auto changing
+ * @param slide
+ */
public addSlide(slide: SlideComponent): void {
this._slides.add(slide);
if (this._slides.length === 1) {
- slide.active = true;
+ this._currentActiveSlide = void 0;
+ this.activeSlide = 0;
this.play();
}
}
+ /**
+ * Removes specified slide. If this slide is active - will roll to another slide
+ * @param slide
+ */
public removeSlide(slide: SlideComponent): void {
const remIndex = this._slides.indexOf(slide);
- if (this.getCurrentSlideIndex() === remIndex) {
+ if (this._currentActiveSlide === remIndex) {
- // behavior in case removing of a current active slide
+ // removing of active slide
+ let nextSlideIndex: number = void 0;
if (this._slides.length > 1) {
- if (this.isLast(remIndex) && this.noWrap) {
-
- // last slide and looping is disabled - step backward
- this._select(Direction.PREV, undefined, true);
- } else {
- this._select(Direction.NEXT, undefined, true);
- }
+ // if this slide last - will roll to first slide, if noWrap flag is FALSE or to previous, if noWrap is TRUE
+ // in case, if this slide in middle of collection, index of next slide is same to removed
+ nextSlideIndex = !this.isLast(remIndex) ? remIndex :
+ this.noWrap ? remIndex - 1 : 0;
}
- }
+ this._slides.remove(remIndex);
+
+ // prevents exception with changing some value after checking
+ setTimeout(() => {
+ this._select(nextSlideIndex);
+ }, 0);
+ } else {
+ this._slides.remove(remIndex);
+ const currentSlideIndex = this.getCurrentSlideIndex();
+ setTimeout(() => {
+ // after removing, need to actualize index of current active slide
+ this._currentActiveSlide = currentSlideIndex;
+ this.activeSlideChange.emit(this._currentActiveSlide);
+ }, 0);
- this._slides.remove(remIndex);
- this.activeSlideChanged.emit(this.getCurrentSlideIndex());
+ }
}
- public nextSlide(): void {
- this._select(Direction.NEXT);
+ /**
+ * Rolling to next slide
+ * @param force: {boolean} if true - will ignore noWrap flag
+ */
+ public nextSlide(force: boolean = false): void {
+ this.activeSlide = this.findNextSlideIndex(Direction.NEXT, force);
}
- public previousSlide(): void {
- this._select(Direction.PREV);
+ /**
+ * Rolling to previous slide
+ * @param force: {boolean} if true - will ignore noWrap flag
+ */
+ public previousSlide(force: boolean = false): void {
+ this.activeSlide = this.findNextSlideIndex(Direction.PREV, force);
}
+ /**
+ * Rolling to specified slide
+ * @param index: {number} index of slide, which must be shown
+ */
public selectSlide(index: number): void {
- this._select(undefined, index, true);
+ this.activeSlide = index;
}
- public play():void {
+ /**
+ * Starts a auto changing of slides
+ */
+ public play(): void {
if (!this.isPlaying) {
this.isPlaying = true;
this.restartTimer();
}
}
- public pause():void {
+ /**
+ * Stops a auto changing of slides
+ */
+ public pause(): void {
if (!this.noPause) {
this.isPlaying = false;
this.resetTimer();
}
}
+ /**
+ * Finds and returns index of currently displayed slide
+ * @returns {number}
+ */
public getCurrentSlideIndex(): number {
return this._slides.findIndex((slide: SlideComponent) => slide.active);
}
+ /**
+ * Defines, whether the specified index is last in collection
+ * @param index
+ * @returns {boolean}
+ */
public isLast(index: number): boolean {
return index + 1 >= this._slides.length;
}
/**
- * Select slide
- * @param direction: {Direction}
- * @param nextIndex: {number}(optional) - index of next active slide
- * @param force: boolean {optional} - if true, selection will ignore this.noWrap flag(for jumping after removing a current slide)
- * @private
+ * Defines next slide index, depending of direction
+ * @param direction: Direction(UNKNOWN|PREV|NEXT)
+ * @param force: {boolean} if TRUE - will ignore noWrap flag, else will return undefined if next slide require wrapping
+ * @returns {any}
*/
- private _select(direction: Direction = 0, nextIndex?: number, force: boolean = false): void {
- const currentSlideIndex = this.getCurrentSlideIndex();
+ private findNextSlideIndex(direction: Direction, force: boolean): number {
+ let nextSlideIndex: number = 0;
- // if this is last slide, need to going forward but looping is disabled
- if (!force && (this.isLast(currentSlideIndex) && direction && direction !== Direction.PREV && this.noWrap)) {
- this.pause();
- return;
+ if (!force && (this.isLast(this.activeSlide) && direction !== Direction.PREV && this.noWrap)) {
+ return void 0;
}
- let currentSlide = this._slides.get(currentSlideIndex);
- let nextSlideIndex: number = !isNaN(nextIndex) ? nextIndex : undefined;
-
- if (direction !== undefined && direction !== Direction.UNKNOWN) {
- switch (direction) {
- case Direction.NEXT:
-
- // if this is last slide, not force, looping is disabled and need to going forward - select current slide, as a next
- nextSlideIndex = (!this.isLast(currentSlideIndex)) ? currentSlideIndex + 1 :
- (!force && this.noWrap ) ? currentSlideIndex : 0;
- break;
- case Direction.PREV:
-
- // if this is first slide, not force, looping is disabled and need to going backward - select current slide, as a next
- nextSlideIndex = (currentSlideIndex > 0) ? currentSlideIndex - 1 :
- (!force && this.noWrap ) ? currentSlideIndex : this._slides.length - 1;
- break;
- default:
- throw new Error('Wrong direction');
- }
+ switch (direction) {
+ case Direction.NEXT:
+ // if this is last slide, not force, looping is disabled and need to going forward - select current slide, as a next
+ nextSlideIndex = (!this.isLast(this._currentActiveSlide)) ? this._currentActiveSlide + 1 :
+ (!force && this.noWrap ) ? this._currentActiveSlide : 0;
+ break;
+ case Direction.PREV:
+ // if this is first slide, not force, looping is disabled and need to going backward - select current slide, as a next
+ nextSlideIndex = (this._currentActiveSlide > 0) ? this._currentActiveSlide - 1 :
+ (!force && this.noWrap ) ? this._currentActiveSlide : this._slides.length - 1;
+ break;
+ default:
+ throw new Error('Unknown direction');
}
+ return nextSlideIndex;
+ }
- if (nextSlideIndex === currentSlideIndex) {
+ /**
+ * Sets a slide, which specified through index, as active
+ * @param index
+ * @private
+ */
+ private _select(index: number): void {
+ if (isNaN(index)) {
+ this.pause();
return;
}
-
- let nextSlide = this._slides.get(nextSlideIndex);
- currentSlide.active = false;
- nextSlide.active = true;
- this.activeSlideChanged.emit(nextSlideIndex);
-
+ let currentSlide = this._slides.get(this._currentActiveSlide);
+ if (currentSlide) {
+ currentSlide.active = false;
+ }
+ let nextSlide = this._slides.get(index);
+ if (nextSlide) {
+ this._currentActiveSlide = index;
+ nextSlide.active = true;
+ this.activeSlide = index;
+ this.activeSlideChange.emit(index);
+ }
}
- private restartTimer():any {
+ /**
+ * Starts loop of auto changing of slides
+ */
+ private restartTimer(): any {
this.resetTimer();
let interval = +this.interval;
if (!isNaN(interval) && interval > 0) {
@@ -208,7 +277,7 @@ export class CarouselComponent implements OnDestroy {
() => {
let nInterval = +this.interval;
if (this.isPlaying && !isNaN(this.interval) && nInterval > 0 && this.slides.length) {
- this._select(Direction.NEXT);
+ this.nextSlide();
} else {
this.pause();
}
@@ -217,7 +286,10 @@ export class CarouselComponent implements OnDestroy {
}
}
- private resetTimer():void {
+ /**
+ * Stops loop of auto changing of slides
+ */
+ private resetTimer(): void {
if (this.currentInterval) {
clearInterval(this.currentInterval);
this.currentInterval = void 0;
diff --git a/src/carousel/carousel.config.ts b/src/carousel/carousel.config.ts
index 32e2702d10..e6a8dafac4 100644
--- a/src/carousel/carousel.config.ts
+++ b/src/carousel/carousel.config.ts
@@ -2,7 +2,12 @@ import { Injectable } from '@angular/core';
@Injectable()
export class CarouselConfig {
+ /** Default interval of auto changing of slides */
public interval: number = 5000;
+
+ /** Is loop of auto changing of slides can be paused */
public noPause: boolean = false;
+
+ /** Is slides can wrap from the last to the first slide */
public noWrap: boolean = false;
}
diff --git a/src/spec/ng-bootstrap/carousel.spec.ts b/src/spec/ng-bootstrap/carousel.spec.ts
new file mode 100644
index 0000000000..71e4825f04
--- /dev/null
+++ b/src/spec/ng-bootstrap/carousel.spec.ts
@@ -0,0 +1,418 @@
+/* tslint:disable:max-classes-per-file max-file-line-count component-class-suffix */
+/**
+ * @copyright Angular ng-bootstrap team
+ */
+import { fakeAsync, discardPeriodicTasks, tick, TestBed, ComponentFixture, inject } from '@angular/core/testing';
+import { createGenericTestComponent } from './test/common';
+
+import { By } from '@angular/platform-browser';
+import { Component } from '@angular/core';
+
+import { CarouselModule, CarouselComponent, CarouselConfig } from '../../carousel';
+
+const createTestComponent = (html: string) =>
+ createGenericTestComponent(html, TestComponent) as ComponentFixture;
+
+function expectActiveSlides(nativeEl: HTMLDivElement, active: boolean[]): void {
+ const slideElms = nativeEl.querySelectorAll('.carousel-item');
+ const indicatorElms = nativeEl.querySelectorAll('ol.carousel-indicators > li');
+
+ expect(slideElms.length).toBe(active.length);
+ expect(indicatorElms.length).toBe(active.length);
+
+ for (let i = 0; i < active.length; i++) {
+ if (active[i]) {
+ expect(slideElms[i]).toHaveCssClass('active');
+ expect(indicatorElms[i]).toHaveCssClass('active');
+ } else {
+ expect(slideElms[i]).not.toHaveCssClass('active');
+ expect(indicatorElms[i]).not.toHaveCssClass('active');
+ }
+ }
+}
+
+describe('ngb-carousel', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({declarations: [TestComponent], imports: [CarouselModule.forRoot()]});
+ });
+
+ it('should initialize inputs with default values', () => {
+ const defaultConfig = new CarouselConfig();
+ const carousel = new CarouselComponent(new CarouselConfig());
+
+ expect(carousel.interval).toBe(defaultConfig.interval);
+ expect(carousel.noWrap).toBe(defaultConfig.noWrap);
+ // expect(carousel.keyboard).toBe(defaultConfig.keyboard);
+ });
+
+ it('should render slides and navigation indicators', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+ const fixture = createTestComponent(html);
+
+ const slideElms = fixture.nativeElement.querySelectorAll('.carousel-item');
+ expect(slideElms.length).toBe(2);
+ expect(slideElms[0].textContent).toMatch(/slide1/);
+ expect(slideElms[1].textContent).toMatch(/slide2/);
+
+ expect(fixture.nativeElement.querySelectorAll('ol.carousel-indicators > li').length).toBe(2);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should mark the first slide as active by default', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should mark the requested slide as active', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ fixture.componentInstance.activeSlideIndex = 1;
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should auto-correct when slide index is undefined', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should change slide on indicator click', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ const indicatorElms = fixture.nativeElement.querySelectorAll('ol.carousel-indicators > li');
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ indicatorElms[1].click();
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should change slide on carousel control click', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ const controlElms = fixture.nativeElement.querySelectorAll('.carousel-control');
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ controlElms[1].click(); // next
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ controlElms[0].click(); // prev
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should change slide on time passage (default interval value)', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(6000);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should change slide on time passage (custom interval value)', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(1000);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(1200);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should not change slide on time passage (custom interval value is zero)', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(1000);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(1200);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should pause / resume slide change with time passage on mouse enter / leave', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ const carouselDebugEl = fixture.debugElement.query(By.directive(CarouselComponent));
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ carouselDebugEl.children[0].triggerEventHandler('mouseenter', {});
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(6000);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ carouselDebugEl.children[0].triggerEventHandler('mouseleave', {});
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ tick(6000);
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+ discardPeriodicTasks();
+ }));
+
+ it('should wrap slide changes by default', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ const controlElms = fixture.nativeElement.querySelectorAll('.carousel-control');
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ controlElms[1].click(); // next
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ controlElms[1].click(); // next
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ controlElms[0].click(); // prev
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ it('should not wrap slide changes by when requested', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+
+ const controlElms = fixture.nativeElement.querySelectorAll('.carousel-control');
+
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ controlElms[0].click(); // prev
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ controlElms[1].click(); // next
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ controlElms[1].click(); // next
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+ }));
+
+ xit('should change on key arrowRight and arrowLeft', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ fixture.debugElement.query(By.directive(CarouselComponent)).triggerEventHandler('keydown.arrowRight', {}); // next()
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ fixture.debugElement.query(By.directive(CarouselComponent)).triggerEventHandler('keydown.arrowLeft', {}); // prev()
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ fixture.componentInstance.keyboard = false;
+ fixture.detectChanges();
+ fixture.debugElement.query(By.directive(CarouselComponent)).triggerEventHandler('keydown.arrowRight', {}); // prev()
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ discardPeriodicTasks();
+
+ }));
+
+ xit('should listen to keyevents based on keyboard attribute', fakeAsync(() => {
+ const html = `
+
+ slide1
+ slide2
+
+ `;
+
+ const fixture = createTestComponent(html);
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ fixture.componentInstance.keyboard = false;
+ fixture.detectChanges();
+ fixture.debugElement.query(By.directive(CarouselComponent)).triggerEventHandler('keydown.arrowRight', {}); // prev()
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [true, false]);
+
+ fixture.componentInstance.keyboard = true;
+ fixture.detectChanges();
+ fixture.debugElement.query(By.directive(CarouselComponent)).triggerEventHandler('keydown.arrowRight', {}); // next()
+ fixture.detectChanges();
+ expectActiveSlides(fixture.nativeElement, [false, true]);
+
+ discardPeriodicTasks();
+
+ }));
+
+ describe('Custom config', () => {
+ let config: CarouselConfig;
+
+ beforeEach(() => { TestBed.configureTestingModule({imports: [CarouselModule.forRoot()]}); });
+
+ beforeEach(inject([CarouselConfig], (c: CarouselConfig) => {
+ config = c;
+ config.interval = 1000;
+ config.noWrap = true;
+ // config.keyboard = false;
+ }));
+
+ it('should initialize inputs with provided config', () => {
+ const fixture = TestBed.createComponent(CarouselComponent);
+ fixture.detectChanges();
+
+ const carousel = fixture.componentInstance;
+ expect(carousel.interval).toBe(config.interval);
+ expect(carousel.noWrap).toBe(config.noWrap);
+ // expect(carousel.keyboard).toBe(config.keyboard);
+ });
+ });
+
+ describe('Custom config as provider', () => {
+ const config = new CarouselConfig();
+ config.interval = 1000;
+ config.noWrap = true;
+ // config.keyboard = false;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule(
+ {imports: [CarouselModule.forRoot()], providers: [{provide: CarouselConfig, useValue: config}]});
+ });
+
+ it('should initialize inputs with provided config as provider', () => {
+ const fixture = TestBed.createComponent(CarouselComponent);
+ fixture.detectChanges();
+
+ const carousel = fixture.componentInstance;
+ expect(carousel.interval).toBe(config.interval);
+ expect(carousel.noWrap).toBe(config.noWrap);
+ // expect(carousel.keyboard).toBe(config.keyboard);
+ });
+ });
+
+});
+
+@Component({selector: 'test-cmp', template: ''})
+class TestComponent {
+ public activeSlideIndex: number;
+ // keyboard = true;
+}
diff --git a/src/spec/ng-bootstrap/utils/linkedlist.spec.ts b/src/spec/utils/linkedlist.spec.ts
similarity index 98%
rename from src/spec/ng-bootstrap/utils/linkedlist.spec.ts
rename to src/spec/utils/linkedlist.spec.ts
index 71af9f8bb8..84378b5bbb 100644
--- a/src/spec/ng-bootstrap/utils/linkedlist.spec.ts
+++ b/src/spec/utils/linkedlist.spec.ts
@@ -1,4 +1,4 @@
-import LinkedList from '../../../utils/linked-list.class';
+import LinkedList from '../../utils/linked-list.class';
let list: LinkedList;
diff --git a/src/utils/linked-list.class.ts b/src/utils/linked-list.class.ts
index a63b5cddd6..6093df856c 100644
--- a/src/utils/linked-list.class.ts
+++ b/src/utils/linked-list.class.ts
@@ -32,7 +32,7 @@ export default class LinkedList extends Array {
public get(position: number): T {
if (this.length === 0 || position < 0 || position >= this.length) {
- throw new Error('Position is out of the list');
+ return void 0;
}
let current = this.head;