Skip to content

Commit

Permalink
Add tests for sticky-header (#6074)
Browse files Browse the repository at this point in the history
* # This is a combination of 9 commits.
# This is the 1st commit message:

# This is a combination of 11 commits.
# This is the 1st commit message:

add lib files for sticky-header

add chose parent

add support to 'optional 'cdkStickyRegion' input '

add app-demo for sticky-header

fix bugs and deleted unused tag id in HTML files

modify

fix some code according to PR review comments

change some format to pass TSlint check

add '_' before private elements

delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable

refine code

encapsulate 'set style for element'

change @input()

Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')'

add const STICK_START_CLASS and STICK_END_CLASS

Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'.

change 'MdStickyHeaderModule' to 'CdkStickyHeaderModule';

encapsulate reset css style operation for sticky header.

delete unnecessary gloable variables

delete global variable '_width'

Add doc for 'sticker()' function. explained how it works.

add more doc for 'sticker()', explaining 'isStuck' flag

2 space for indent

# This is the commit message #2:

fix
# This is the commit message #3:

delete sticky-header demo part from this branch

# This is the commit message #4:

revert firebase file
# This is the commit message #5:

change code according to comments in PR
# This is the commit message #6:

revert firbaserc
# This is the commit message #7:

revert demo-app.ts
# This is the commit message #8:

revert routes.ts
# This is the commit message #9:

revert demo-app-module.ts


# This is the commit message #10:

change
# This is the commit message #11:

fix the problem of : 'this.stickyParent' might be 'null'
# This is the commit message #2:

change doc
# This is the commit message #3:

Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }'
# This is the commit message #4:

Added prefix 'mat-' for CSS class
# This is the commit message #5:

Delete 'public' before variables
# This is the commit message #6:

Object.assign isn't supported in IE11; use extendObject from src/lib/core/util.
# This is the commit message #7:

 IE11 will have trouble with  `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);'
# This is the commit message #8:

Added docs for all variables
# This is the commit message #9:

extract 'generate CSS style'

* # This is a combination of 5 commits.
# This is the 1st commit message:

add lib files for sticky-header

add chose parent

add support to 'optional 'cdkStickyRegion' input '

add app-demo for sticky-header

fix bugs and deleted unused tag id in HTML files

modify

fix some code according to PR review comments

change some format to pass TSlint check

add '_' before private elements

delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable

refine code

encapsulate 'set style for element'

change @input()

Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')'

add const STICK_START_CLASS and STICK_END_CLASS

Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'.

change 'MdStickyHeaderModule' to 'CdkStickyHeaderModule';

encapsulate reset css style operation for sticky header.

delete unnecessary gloable variables

delete global variable '_width'

Add doc for 'sticker()' function. explained how it works.

add more doc for 'sticker()', explaining 'isStuck' flag

2 space for indent

fix

delete sticky-header demo part from this branch

revert firebase file

change code according to comments in PR

revert firbaserc

revert demo-app.ts

revert routes.ts

revert demo-app-module.ts

change

fix the problem of : 'this.stickyParent' might be 'null'

change doc

Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }'

Added prefix 'mat-' for CSS class

Delete 'public' before variables

Object.assign isn't supported in IE11; use extendObject from src/lib/core/util.

 IE11 will have trouble with  `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);'

Added docs for all variables

extract 'generate CSS style'

created a generateStyleCSS() function, let it be responsible for generating all those CSS styles.

reformat

add debounce to solve 'getBoundingClientRect() cause slow down' problem.

add position:sticky and check whether browser support it. If not , use the naive implementation

removed unused import

Removed unused 'scrollableRegion' and 'parentRegion'

removed commented lines

default public

Add comments about why setting style top and position for iPhone and not IE. And extract detectBrowser() as a new function

format

consider all circumstances of browser.

use "===" instead of '=='

 make 'navigator.userAgent.toLocaleLowerCase()' a local variable

optimize

Added comments on const 'STICK_START_CLASS' and 'STICK_END_CLASS'.
change their content to cdk-sticky-header-start and cdk-sticky-header-end

Added comments for STICK_START_CLASS and STICK_END_CLASS.

Changed the format of one-line JsDoc

unsubscribe sbscriptions onDestory

Use what modernizr does on compatibility instead of get the browser version directly.

add 'padding' and 'stickyRegionHeight' variables to avoid calling 'getComputedStyle()' too many times (which is expensive).

move docs above @directive

removed the underscore in'_element: ElementRef',

expand 'reg' to 'region'

use 'if (this.isIE)' instead of 'if(this.isIE === true)'

added more newlines between params in 'generateCssStyle()' function to make it easier to understand.

Added reference link to Modernizer in docs of getSupportList()

Deleted "_supportList" variable

renamed 'isIE' to 'isStickyPositionSupported', and removed extra space before Observable

Set debounce time as a const variable

Added docs for 'const DEBOUNCE_TIME: number = 5;'

Changed ' if(this.stickyParent == null)' to ' if(!this.stickyParent)'

 Removed the @param and @returns and make sure the types are correct in the function signature in 'generateCssStyle(...)' function

Added docs for `isStickyPositionSupported` variable

changed '+=' to '=' of 'stickyText' in getSupportList() function

nit added " " between 'if' and '('

nit

Added comments

deleted unused import

change comments

optimize comments

deleted unnecessary global variables(padding and stickyRegionHeight)

Added check whether we are on browser

Array<string> to string[]

test?

try to reopen the old PR

fix after rebase

revert list.ts

test

test 222

revert demo

revert list.ts second time

Move code to 'src/cdk'

revert 'move code to 'src/cdk'' , it should be done in a new PR

revert

avoid calling 'getComputedStyle()' too many times.

rename as sticky-header.ts

# This is the commit message #2:

imported PlatformModule
# This is the commit message #3:

Add blank lines between these top-level symbol
# This is the commit message #4:

make '_isStickyPositionSupported' private
# This is the commit message #5:

Changed the originalCSS to private and use '{} as CSSStyleDeclaration' instead of ''any.

* # This is a combination of 16 commits.
# This is the 1st commit message:

# This is a combination of 10 commits.
# This is the 1st commit message:

add lib files for sticky-header

add chose parent

add support to 'optional 'cdkStickyRegion' input '

add app-demo for sticky-header

fix bugs and deleted unused tag id in HTML files

modify

fix some code according to PR review comments

change some format to pass TSlint check

add '_' before private elements

delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable

refine code

encapsulate 'set style for element'

change @input()

Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')'

add const STICK_START_CLASS and STICK_END_CLASS

Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'.

change 'MdStickyHeaderModule' to 'CdkStickyHeaderModule';

encapsulate reset css style operation for sticky header.

delete unnecessary gloable variables

delete global variable '_width'

Add doc for 'sticker()' function. explained how it works.

add more doc for 'sticker()', explaining 'isStuck' flag

2 space for indent

fix

delete sticky-header demo part from this branch

revert firebase file

change code according to comments in PR

revert firbaserc

revert demo-app.ts

revert routes.ts

revert demo-app-module.ts

change

fix the problem of : 'this.stickyParent' might be 'null'

change doc

Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }'

Added prefix 'mat-' for CSS class

Delete 'public' before variables

Object.assign isn't supported in IE11; use extendObject from src/lib/core/util.

 IE11 will have trouble with  `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);'

Added docs for all variables

extract 'generate CSS style'

created a generateStyleCSS() function, let it be responsible for generating all those CSS styles.

reformat

add debounce to solve 'getBoundingClientRect() cause slow down' problem.

add position:sticky and check whether browser support it. If not , use the naive implementation

removed unused import

Removed unused 'scrollableRegion' and 'parentRegion'

removed commented lines

default public

Add comments about why setting style top and position for iPhone and not IE. And extract detectBrowser() as a new function

format

consider all circumstances of browser.

use "===" instead of '=='

 make 'navigator.userAgent.toLocaleLowerCase()' a local variable

optimize

Added comments on const 'STICK_START_CLASS' and 'STICK_END_CLASS'.
change their content to cdk-sticky-header-start and cdk-sticky-header-end

Added comments for STICK_START_CLASS and STICK_END_CLASS.

Changed the format of one-line JsDoc

unsubscribe sbscriptions onDestory

Use what modernizr does on compatibility instead of get the browser version directly.

add 'padding' and 'stickyRegionHeight' variables to avoid calling 'getComputedStyle()' too many times (which is expensive).

move docs above @directive

removed the underscore in'_element: ElementRef',

expand 'reg' to 'region'

use 'if (this.isIE)' instead of 'if(this.isIE === true)'

added more newlines between params in 'generateCssStyle()' function to make it easier to understand.

Added reference link to Modernizer in docs of getSupportList()

Deleted "_supportList" variable

renamed 'isIE' to 'isStickyPositionSupported', and removed extra space before Observable

Set debounce time as a const variable

Added docs for 'const DEBOUNCE_TIME: number = 5;'

Changed ' if(this.stickyParent == null)' to ' if(!this.stickyParent)'

 Removed the @param and @returns and make sure the types are correct in the function signature in 'generateCssStyle(...)' function

Added docs for `isStickyPositionSupported` variable

changed '+=' to '=' of 'stickyText' in getSupportList() function

nit added " " between 'if' and '('

nit

Added comments

deleted unused import

change comments

optimize comments

deleted unnecessary global variables(padding and stickyRegionHeight)

Added check whether we are on browser

Array<string> to string[]

test?

try to reopen the old PR

fix after rebase

revert list.ts

test

test 222

revert demo

revert list.ts second time

Move code to 'src/cdk'

revert 'move code to 'src/cdk'' , it should be done in a new PR

revert

avoid calling 'getComputedStyle()' too many times.

rename as sticky-header.ts

imported PlatformModule

Add blank lines between these top-level symbol

make '_isStickyPositionSupported' private

Changed the originalCSS to private and use '{} as CSSStyleDeclaration' instead of ''any.

Rename '_containerStart' to '_stickyRegionTop'

# This is the commit message #2:

rename
# This is the commit message #3:

optimize discription for '_stickyRegionBottomThreshold'
# This is the commit message #4:

private _originalStyles = {
      position: '',
      top: '',
      right: '',
      left: '',
      bottom: '',
      width:  '',
      zIndex: ''};
# This is the commit message #5:

Deleted 'generateCssStyle()' and 'getCssNumber()' function
# This is the commit message #6:

Deleted 'getCssValue()' function
# This is the commit message #7:

fix CSSStyleDeclaration


# This is the commit message #8:

change sticky width to 'this.upperScrollableContainer.clientWidth'
# This is the commit message #9:

fix
# This is the commit message #10:

nit
# This is the commit message #2:

Added the 'isPositionStickySupported() ' function  to src/cdk/platform/features.ts.
Consume that function in this component and just always use both the webkit and unprefixed styles.
# This is the commit message #3:

nit


# This is the commit message #4:

nit
# This is the commit message #5:

update doc 'Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the positioning adversely.'
# This is the commit message #6:

changed the doc to  '/** z-index to be applied to the sticky header (default is 10). */'
# This is the commit message #7:

fix tslint error
# This is the commit message #8:

for comment 'Can you evaluate each method to make sure their accessor privacy is right? E.g. see which functions need to be public, private, static, etc'


# This is the commit message #9:

Deleted variable 'elemHeight'
# This is the commit message #10:

Chaned to 'if (!this.stickyParent)'
# This is the commit message #11:

Simplified Docs for 'sticker()'.
# This is the commit message #12:

set 'defineRestriction()' function to private
# This is the commit message #13:

use 'RxChain'
# This is the commit message #14:

deleted unused 'tableModule' in modules.ts
# This is the commit message #15:

rename to  '_isPositionStickySupported'
# This is the commit message #16:

Use // for comments, /* */ for docs

* add lib files for sticky-header

add chose parent

add support to 'optional 'cdkStickyRegion' input '

add app-demo for sticky-header

fix bugs and deleted unused tag id in HTML files

modify

fix some code according to PR review comments

change some format to pass TSlint check

add '_' before private elements

delete @Injectable for StickyHeaderDirective. Because we do not need @Injectable

refine code

encapsulate 'set style for element'

change @input()

Delete 'Observable.fromEvent(this.upperScrollableContainer, 'scroll')'

add const STICK_START_CLASS and STICK_END_CLASS

Add doc for [cdkStickyRegion] and 'unstuckElement()'. Delete 'detach()' function, add its content into 'ngOnDestroy()'.

change 'MdStickyHeaderModule' to 'CdkStickyHeaderModule';

encapsulate reset css style operation for sticky header.

delete unnecessary gloable variables

delete global variable '_width'

Add doc for 'sticker()' function. explained how it works.

add more doc for 'sticker()', explaining 'isStuck' flag

2 space for indent

fix

delete sticky-header demo part from this branch

revert firebase file

change code according to comments in PR

revert firbaserc

revert demo-app.ts

revert routes.ts

revert demo-app-module.ts

change

fix the problem of : 'this.stickyParent' might be 'null'

change doc

Change the constructor of 'cdkStickyRegion' to 'constructor(public readonly _elementRef: ElementRef) { }'

Added prefix 'mat-' for CSS class

Delete 'public' before variables

Object.assign isn't supported in IE11; use extendObject from src/lib/core/util.

 IE11 will have trouble with  `translate3d(0, 0, 0);', change to `translate3d(0px, 0px, 0px);'

Added docs for all variables

extract 'generate CSS style'

created a generateStyleCSS() function, let it be responsible for generating all those CSS styles.

reformat

add debounce to solve 'getBoundingClientRect() cause slow down' problem.

add position:sticky and check whether browser support it. If not , use the naive implementation

removed unused import

Removed unused 'scrollableRegion' and 'parentRegion'

removed commented lines

default public

Add comments about why setting style top and position for iPhone and not IE. And extract detectBrowser() as a new function

format

consider all circumstances of browser.

use "===" instead of '=='

 make 'navigator.userAgent.toLocaleLowerCase()' a local variable

optimize

Added comments on const 'STICK_START_CLASS' and 'STICK_END_CLASS'.
change their content to cdk-sticky-header-start and cdk-sticky-header-end

Added comments for STICK_START_CLASS and STICK_END_CLASS.

Changed the format of one-line JsDoc

unsubscribe sbscriptions onDestory

Use what modernizr does on compatibility instead of get the browser version directly.

add 'padding' and 'stickyRegionHeight' variables to avoid calling 'getComputedStyle()' too many times (which is expensive).

move docs above @directive

removed the underscore in'_element: ElementRef',

expand 'reg' to 'region'

use 'if (this.isIE)' instead of 'if(this.isIE === true)'

added more newlines between params in 'generateCssStyle()' function to make it easier to understand.

Added reference link to Modernizer in docs of getSupportList()

Deleted "_supportList" variable

renamed 'isIE' to 'isStickyPositionSupported', and removed extra space before Observable

Set debounce time as a const variable

Added docs for 'const DEBOUNCE_TIME: number = 5;'

Changed ' if(this.stickyParent == null)' to ' if(!this.stickyParent)'

 Removed the @param and @returns and make sure the types are correct in the function signature in 'generateCssStyle(...)' function

Added docs for `isStickyPositionSupported` variable

changed '+=' to '=' of 'stickyText' in getSupportList() function

nit added " " between 'if' and '('

nit

Added comments

deleted unused import

change comments

optimize comments

deleted unnecessary global variables(padding and stickyRegionHeight)

Added check whether we are on browser

Array<string> to string[]

test?

try to reopen the old PR

fix after rebase

revert list.ts

test

test 222

revert demo

revert list.ts second time

Move code to 'src/cdk'

revert 'move code to 'src/cdk'' , it should be done in a new PR

revert

avoid calling 'getComputedStyle()' too many times.

rename as sticky-header.ts

imported PlatformModule

Add blank lines between these top-level symbol

make '_isStickyPositionSupported' private

Changed the originalCSS to private and use '{} as CSSStyleDeclaration' instead of ''any.

Rename '_containerStart' to '_stickyRegionTop'

rename

optimize discription for '_stickyRegionBottomThreshold'

private _originalStyles = {
      position: '',
      top: '',
      right: '',
      left: '',
      bottom: '',
      width:  '',
      zIndex: ''};

Deleted 'generateCssStyle()' and 'getCssNumber()' function

Deleted 'getCssValue()' function

fix CSSStyleDeclaration

change sticky width to 'this.upperScrollableContainer.clientWidth'

fix

nit

Added the 'isPositionStickySupported() ' function  to src/cdk/platform/features.ts.
Consume that function in this component and just always use both the webkit and unprefixed styles.

nit

nit

update doc 'Debounce time in milliseconds for events that affect the sticky positioning (e.g. scroll, resize, touch move). Set as 5 milliseconds which is the highest delay that doesn't drastically affect the positioning adversely.'

changed the doc to  '/** z-index to be applied to the sticky header (default is 10). */'

fix tslint error

for comment 'Can you evaluate each method to make sure their accessor privacy is right? E.g. see which functions need to be public, private, static, etc'

Deleted variable 'elemHeight'

Chaned to 'if (!this.stickyParent)'

Simplified Docs for 'sticker()'.

set 'defineRestriction()' function to private

use 'RxChain'

deleted unused 'tableModule' in modules.ts

rename to  '_isPositionStickySupported'

Use // for comments, /* */ for docs

@angular/cdk

rename : values -> headerStyles

Move closing brace to the next line

optimized: [this._onScrollSubscription, this._onScrollSubscription, this._onResizeSubscription]
        .forEach(s => s && s.unsubscribe());

You should be able to do just '0' instead of '0px'

Format like TODO(sllethe): ...

private _attachEventListeners? Add a description like "Add listeners for events that affect sticky positioning."

optimize doc

Rename 'defineRestrictions' to '_measureStickyRegionBounds'

rename: private _resetElementStyles

let stuckRight: any = this.upperScrollableContainer.getBoundingClientRect().right;
chaned 'any' to 'number'

nit

change doc '/**
   * Unsticks the header so that it goes back to scrolling normally.
   *
   * This should be called when the element reaches the bottom of its cdkStickyRegion so that it
   * smoothly scrolls out of view as the next sticky-header moves in.
   */'

_unstuckElement -> _unstickElement

rename 'sticker()' to '_applyStickyPositionStyles()'

rename 'defineRestrictionsAndStick()' to '_updateStickyPositioning()'

* added tests for sticky-header

* deleted

* removed 'deps' in providers

* put provider in index

* optimize provider

* Removed unused 'fixture.detectChanges()' in test

* fix

* You can remove when sticky positioning is not supported since it's in describe

* stickyHeaderDir.stickyParent

* remove commented code

* rename stickyHeaderDir to stickyHeader

* Changed to use 'ngFor' to expand content instead of typing tons of '<p></p>'

* Add a comment that explains what these inline styles are for? Any reason you can't set them via the styles configuration for the component?

* Added explainations on styles

* Added /** @docs-private */

*  Added comments to explaining why tick(100) is needed. Changed 'tick(0)' to 'tick()'

* nit

* explained why need 'tick(100)'

* expect(/sticky/i.test(position!)).toBe(true);

* change CSS indent to +2

* nit

* Added test for sticky-header without StickyRegion

* change 'toBe(null)' to 'toBeNull()'

* Cool ! tick(debounce time)
  • Loading branch information
sllethe authored Aug 4, 2017
1 parent 54b6c84 commit c8185fb
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 12 deletions.
8 changes: 6 additions & 2 deletions src/lib/sticky-header/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {OverlayModule, MdCommonModule, PlatformModule} from '../core';
import {CdkStickyRegion, CdkStickyHeader} from './sticky-header';

import {
CdkStickyRegion,
CdkStickyHeader,
STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER
} from './sticky-header';


@NgModule({
imports: [OverlayModule, MdCommonModule, CommonModule, PlatformModule],
declarations: [CdkStickyRegion, CdkStickyHeader],
exports: [CdkStickyRegion, CdkStickyHeader, MdCommonModule],
providers: [STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER]
})
export class StickyHeaderModule {}

Expand Down
253 changes: 253 additions & 0 deletions src/lib/sticky-header/sticky-header.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {Component, DebugElement, ViewChild} from '@angular/core';
import {
StickyHeaderModule,
CdkStickyRegion,
CdkStickyHeader,
STICKY_HEADER_SUPPORT_STRATEGY
} from './index';
import {OverlayModule, Scrollable} from '../core/overlay/index';
import {PlatformModule} from '../core/platform/index';
import {By} from '@angular/platform-browser';
import {dispatchFakeEvent} from '@angular/cdk/testing';

const DEBOUNCE_TIME: number = 5;

describe('sticky-header with positioning not supported', () => {
let fixture: ComponentFixture<StickyHeaderTest>;
let testComponent: StickyHeaderTest;
let stickyElement: DebugElement;
let stickyParentElement: DebugElement;
let scrollableElement: DebugElement;
let stickyHeader: CdkStickyHeader;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
declarations: [StickyHeaderTest],
providers: [
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: false},
],
});
TestBed.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(StickyHeaderTest);
fixture.detectChanges();
testComponent = fixture.debugElement.componentInstance;
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
stickyParentElement = fixture.debugElement.query(By.directive(CdkStickyRegion));
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
scrollableElement = fixture.debugElement.query(By.directive(Scrollable));
});

it('should be able to find stickyParent', () => {
expect(stickyHeader.stickyParent).not.toBeNull();
});

it('should be able to find scrollableContainer', () => {
expect(stickyHeader.upperScrollableContainer).not.toBeNull();
});

it('should stick in the right place when scrolled to the top of the container', fakeAsync(() => {
let scrollableContainerTop = stickyHeader.upperScrollableContainer
.getBoundingClientRect().top;
expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);
tick();

// Scroll the scrollableContainer up to stick
fixture.componentInstance.scrollDown();
// wait for the DEBOUNCE_TIME
tick(DEBOUNCE_TIME);

expect(stickyHeader.element.getBoundingClientRect().top).toBe(scrollableContainerTop);
}));

it('should unstuck when scrolled off the top of the container', fakeAsync(() => {
let scrollableContainerTop = stickyHeader.upperScrollableContainer
.getBoundingClientRect().top;
expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);
tick();

// Scroll the scrollableContainer up to stick
fixture.componentInstance.scrollDown();
// wait for the DEBOUNCE_TIME
tick(DEBOUNCE_TIME);

expect(stickyHeader.element.getBoundingClientRect().top).toBe(scrollableContainerTop);

// Scroll the scrollableContainer down to unstuck
fixture.componentInstance.scrollBack();
tick(DEBOUNCE_TIME);

expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);

}));
});

describe('sticky-header with positioning supported', () => {
let fixture: ComponentFixture<StickyHeaderTest>;
let testComponent: StickyHeaderTest;
let stickyElement: DebugElement;
let stickyParentElement: DebugElement;
let stickyHeader: CdkStickyHeader;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
declarations: [StickyHeaderTest],
providers: [
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: true},
],
});
TestBed.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(StickyHeaderTest);
fixture.detectChanges();
testComponent = fixture.debugElement.componentInstance;
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
stickyParentElement = fixture.debugElement.query(By.directive(CdkStickyRegion));
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
});

it('should find sticky positioning is applied', () => {
let position = window.getComputedStyle(stickyHeader.element).position;
expect(position).not.toBeNull();
expect(/sticky/i.test(position!)).toBe(true);
});
});

describe('test sticky-header without StickyRegion', () => {
let fixture: ComponentFixture<StickyHeaderTestNoStickyRegion>;
let testComponent: StickyHeaderTestNoStickyRegion;
let stickyElement: DebugElement;
let stickyHeader: CdkStickyHeader;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
declarations: [StickyHeaderTestNoStickyRegion],
providers: [
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: false},
],
});
TestBed.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(StickyHeaderTestNoStickyRegion);
fixture.detectChanges();
testComponent = fixture.debugElement.componentInstance;
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
});

it('should be able to find stickyParent', () => {
let p = stickyHeader.stickyParent;
expect(p).not.toBeNull();
expect(p!.id).toBe('default-region');
});
});

@Component({
// Use styles to define the style of scrollable container and header,
// which help test to make sure whether the header is stuck at the right position.
styles:[`
.scrollable-style {
text-align: center;
-webkit-appearance: none;
-moz-appearance: none;
height: 300px;
overflow: auto;
}
.heading-style {
background: whitesmoke;
padding: 5px;
}
`],
template: `
<div cdk-scrollable class="scrollable-style">
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<div cdkStickyRegion>
<div cdkStickyHeader class="heading-style">
<h2>Heading 1</h2>
</div>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
</div>
</div>
`})
class StickyHeaderTest {
@ViewChild(Scrollable) scrollingContainer: Scrollable;

items: any[] = [
{'name': 'Forrest', 'message': 'Life was like a box of chocolates'},
{'name': 'Gump', 'message': 'you never know what you are gonna get'},
{'name': 'Lion King', 'message': 'Everything you see exists together'},
{'name': 'Jack', 'message': 'in a delicate balance'},
{'name': 'Garfield', 'message': 'Save Water'},
{'name': 'Shawshank', 'message': 'There is something inside'},
{'name': 'Jone', 'message': 'Enough movies?'},
];

scrollDown() {
const scrollingContainerEl = this.scrollingContainer.getElementRef().nativeElement;
scrollingContainerEl.scrollTop = 300;

// Emit a scroll event from the scrolling element in our component.
dispatchFakeEvent(scrollingContainerEl, 'scroll');
}

scrollBack() {
const scrollingContainerEl = this.scrollingContainer.getElementRef().nativeElement;
scrollingContainerEl.scrollTop = 0;

// Emit a scroll event from the scrolling element in our component.
dispatchFakeEvent(scrollingContainerEl, 'scroll');
}
}

@Component({
// Use styles to define the style of scrollable container and header,
// which help test to make sure whether the header is stuck at the right position.
styles:[`
.scrollable-style {
text-align: center;
-webkit-appearance: none;
-moz-appearance: none;
height: 300px;
overflow: auto;
}
.heading-style {
background: whitesmoke;
padding: 5px;
}
`],
template: `
<div cdk-scrollable class="scrollable-style">
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<div id="default-region">
<div cdkStickyHeader class="heading-style">
<h2>Heading 1</h2>
</div>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
</div>
</div>
`})
class StickyHeaderTestNoStickyRegion {
items: any[] = [
{'name': 'Forrest', 'message': 'Life was like a box of chocolates'},
{'name': 'Gump', 'message': 'you never know what you are gonna get'},
{'name': 'Lion King', 'message': 'Everything you see exists together'},
{'name': 'Jack', 'message': 'in a delicate balance'},
{'name': 'Garfield', 'message': 'Save Water'},
{'name': 'Shawshank', 'message': 'There is something inside'},
{'name': 'Jone', 'message': 'Enough movies?'},
];
}
28 changes: 18 additions & 10 deletions src/lib/sticky-header/sticky-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Input,
OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core';
import {Platform} from '../core/platform';
OnDestroy, AfterViewInit, ElementRef, Optional,
InjectionToken, Injectable, Inject, Provider} from '@angular/core';
import {Platform} from '../core/platform/index';
import {Scrollable} from '../core/overlay/scroll/scrollable';
import {extendObject} from '../core/util/object-extend';
import {Subscription} from 'rxjs/Subscription';
Expand All @@ -32,7 +33,6 @@ export class CdkStickyRegion {
constructor(public readonly _elementRef: ElementRef) { }
}


/** Class applied when the header is "stuck" */
const STICK_START_CLASS = 'cdk-sticky-header-start';

Expand All @@ -46,6 +46,16 @@ const STICK_END_CLASS = 'cdk-sticky-header-end';
*/
const DEBOUNCE_TIME: number = 5;

export const STICKY_HEADER_SUPPORT_STRATEGY = new InjectionToken('sticky-header-support-strategy');

/** @docs-private
* Create a factory for sticky-positioning check to make code more testable
*/
export const STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER: Provider = {
provide: STICKY_HEADER_SUPPORT_STRATEGY,
useFactory: isPositionStickySupported
};

/**
* Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with
* cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky
Expand All @@ -61,8 +71,6 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {

/** boolean value to mark whether the current header is stuck*/
isStuck: boolean = false;
/** Whether the browser support CSS sticky positioning. */
private _isPositionStickySupported: boolean = false;

/** The element with the 'cdkStickyHeader' tag. */
element: HTMLElement;
Expand Down Expand Up @@ -97,7 +105,8 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
constructor(element: ElementRef,
scrollable: Scrollable,
@Optional() public parentRegion: CdkStickyRegion,
platform: Platform) {
platform: Platform,
@Inject(STICKY_HEADER_SUPPORT_STRATEGY) public _isPositionStickySupported) {
if (platform.isBrowser) {
this.element = element.nativeElement;
this.upperScrollableContainer = scrollable.getElementRef().nativeElement;
Expand Down Expand Up @@ -137,7 +146,6 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
* sticky positioning. If not, use the original implementation.
*/
private _setStrategyAccordingToCompatibility(): void {
this._isPositionStickySupported = isPositionStickySupported();
if (this._isPositionStickySupported) {
this.element.style.top = '0';
this.element.style.cssText += 'position: -webkit-sticky; position: sticky; ';
Expand Down Expand Up @@ -258,9 +266,9 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {


/**
* '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when
* a header should be stick and when should it be unstuck by comparing the offsetTop
* of scrollable container with the top and bottom of the sticky region.
* '_applyStickyPositionStyles()' function contains the main logic of sticky-header.
* It decides when a header should be stick and when should it be unstuck by comparing
* the offsetTop of scrollable container with the top and bottom of the sticky region.
*/
_applyStickyPositionStyles(): void {
let currentPosition: number = this.upperScrollableContainer.offsetTop;
Expand Down

0 comments on commit c8185fb

Please sign in to comment.