Skip to content

Commit c8185fb

Browse files
authored
Add tests for sticky-header (#6074)
* # 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)
1 parent 54b6c84 commit c8185fb

File tree

3 files changed

+277
-12
lines changed

3 files changed

+277
-12
lines changed

src/lib/sticky-header/index.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88
import {NgModule} from '@angular/core';
99
import {CommonModule} from '@angular/common';
1010
import {OverlayModule, MdCommonModule, PlatformModule} from '../core';
11-
import {CdkStickyRegion, CdkStickyHeader} from './sticky-header';
12-
11+
import {
12+
CdkStickyRegion,
13+
CdkStickyHeader,
14+
STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER
15+
} from './sticky-header';
1316

1417

1518
@NgModule({
1619
imports: [OverlayModule, MdCommonModule, CommonModule, PlatformModule],
1720
declarations: [CdkStickyRegion, CdkStickyHeader],
1821
exports: [CdkStickyRegion, CdkStickyHeader, MdCommonModule],
22+
providers: [STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER]
1923
})
2024
export class StickyHeaderModule {}
2125

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
2+
import {Component, DebugElement, ViewChild} from '@angular/core';
3+
import {
4+
StickyHeaderModule,
5+
CdkStickyRegion,
6+
CdkStickyHeader,
7+
STICKY_HEADER_SUPPORT_STRATEGY
8+
} from './index';
9+
import {OverlayModule, Scrollable} from '../core/overlay/index';
10+
import {PlatformModule} from '../core/platform/index';
11+
import {By} from '@angular/platform-browser';
12+
import {dispatchFakeEvent} from '@angular/cdk/testing';
13+
14+
const DEBOUNCE_TIME: number = 5;
15+
16+
describe('sticky-header with positioning not supported', () => {
17+
let fixture: ComponentFixture<StickyHeaderTest>;
18+
let testComponent: StickyHeaderTest;
19+
let stickyElement: DebugElement;
20+
let stickyParentElement: DebugElement;
21+
let scrollableElement: DebugElement;
22+
let stickyHeader: CdkStickyHeader;
23+
24+
beforeEach(async(() => {
25+
TestBed.configureTestingModule({
26+
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
27+
declarations: [StickyHeaderTest],
28+
providers: [
29+
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: false},
30+
],
31+
});
32+
TestBed.compileComponents();
33+
}));
34+
35+
beforeEach(() => {
36+
fixture = TestBed.createComponent(StickyHeaderTest);
37+
fixture.detectChanges();
38+
testComponent = fixture.debugElement.componentInstance;
39+
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
40+
stickyParentElement = fixture.debugElement.query(By.directive(CdkStickyRegion));
41+
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
42+
scrollableElement = fixture.debugElement.query(By.directive(Scrollable));
43+
});
44+
45+
it('should be able to find stickyParent', () => {
46+
expect(stickyHeader.stickyParent).not.toBeNull();
47+
});
48+
49+
it('should be able to find scrollableContainer', () => {
50+
expect(stickyHeader.upperScrollableContainer).not.toBeNull();
51+
});
52+
53+
it('should stick in the right place when scrolled to the top of the container', fakeAsync(() => {
54+
let scrollableContainerTop = stickyHeader.upperScrollableContainer
55+
.getBoundingClientRect().top;
56+
expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);
57+
tick();
58+
59+
// Scroll the scrollableContainer up to stick
60+
fixture.componentInstance.scrollDown();
61+
// wait for the DEBOUNCE_TIME
62+
tick(DEBOUNCE_TIME);
63+
64+
expect(stickyHeader.element.getBoundingClientRect().top).toBe(scrollableContainerTop);
65+
}));
66+
67+
it('should unstuck when scrolled off the top of the container', fakeAsync(() => {
68+
let scrollableContainerTop = stickyHeader.upperScrollableContainer
69+
.getBoundingClientRect().top;
70+
expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);
71+
tick();
72+
73+
// Scroll the scrollableContainer up to stick
74+
fixture.componentInstance.scrollDown();
75+
// wait for the DEBOUNCE_TIME
76+
tick(DEBOUNCE_TIME);
77+
78+
expect(stickyHeader.element.getBoundingClientRect().top).toBe(scrollableContainerTop);
79+
80+
// Scroll the scrollableContainer down to unstuck
81+
fixture.componentInstance.scrollBack();
82+
tick(DEBOUNCE_TIME);
83+
84+
expect(stickyHeader.element.getBoundingClientRect().top).not.toBe(scrollableContainerTop);
85+
86+
}));
87+
});
88+
89+
describe('sticky-header with positioning supported', () => {
90+
let fixture: ComponentFixture<StickyHeaderTest>;
91+
let testComponent: StickyHeaderTest;
92+
let stickyElement: DebugElement;
93+
let stickyParentElement: DebugElement;
94+
let stickyHeader: CdkStickyHeader;
95+
96+
beforeEach(async(() => {
97+
TestBed.configureTestingModule({
98+
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
99+
declarations: [StickyHeaderTest],
100+
providers: [
101+
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: true},
102+
],
103+
});
104+
TestBed.compileComponents();
105+
}));
106+
107+
beforeEach(() => {
108+
fixture = TestBed.createComponent(StickyHeaderTest);
109+
fixture.detectChanges();
110+
testComponent = fixture.debugElement.componentInstance;
111+
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
112+
stickyParentElement = fixture.debugElement.query(By.directive(CdkStickyRegion));
113+
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
114+
});
115+
116+
it('should find sticky positioning is applied', () => {
117+
let position = window.getComputedStyle(stickyHeader.element).position;
118+
expect(position).not.toBeNull();
119+
expect(/sticky/i.test(position!)).toBe(true);
120+
});
121+
});
122+
123+
describe('test sticky-header without StickyRegion', () => {
124+
let fixture: ComponentFixture<StickyHeaderTestNoStickyRegion>;
125+
let testComponent: StickyHeaderTestNoStickyRegion;
126+
let stickyElement: DebugElement;
127+
let stickyHeader: CdkStickyHeader;
128+
129+
beforeEach(async(() => {
130+
TestBed.configureTestingModule({
131+
imports: [ OverlayModule, PlatformModule, StickyHeaderModule ],
132+
declarations: [StickyHeaderTestNoStickyRegion],
133+
providers: [
134+
{provide: STICKY_HEADER_SUPPORT_STRATEGY, useValue: false},
135+
],
136+
});
137+
TestBed.compileComponents();
138+
}));
139+
140+
beforeEach(() => {
141+
fixture = TestBed.createComponent(StickyHeaderTestNoStickyRegion);
142+
fixture.detectChanges();
143+
testComponent = fixture.debugElement.componentInstance;
144+
stickyElement = fixture.debugElement.query(By.directive(CdkStickyHeader));
145+
stickyHeader = stickyElement.injector.get<CdkStickyHeader>(CdkStickyHeader);
146+
});
147+
148+
it('should be able to find stickyParent', () => {
149+
let p = stickyHeader.stickyParent;
150+
expect(p).not.toBeNull();
151+
expect(p!.id).toBe('default-region');
152+
});
153+
});
154+
155+
@Component({
156+
// Use styles to define the style of scrollable container and header,
157+
// which help test to make sure whether the header is stuck at the right position.
158+
styles:[`
159+
.scrollable-style {
160+
text-align: center;
161+
-webkit-appearance: none;
162+
-moz-appearance: none;
163+
height: 300px;
164+
overflow: auto;
165+
}
166+
.heading-style {
167+
background: whitesmoke;
168+
padding: 5px;
169+
}
170+
`],
171+
template: `
172+
<div cdk-scrollable class="scrollable-style">
173+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
174+
<div cdkStickyRegion>
175+
<div cdkStickyHeader class="heading-style">
176+
<h2>Heading 1</h2>
177+
</div>
178+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
179+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
180+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
181+
</div>
182+
</div>
183+
`})
184+
class StickyHeaderTest {
185+
@ViewChild(Scrollable) scrollingContainer: Scrollable;
186+
187+
items: any[] = [
188+
{'name': 'Forrest', 'message': 'Life was like a box of chocolates'},
189+
{'name': 'Gump', 'message': 'you never know what you are gonna get'},
190+
{'name': 'Lion King', 'message': 'Everything you see exists together'},
191+
{'name': 'Jack', 'message': 'in a delicate balance'},
192+
{'name': 'Garfield', 'message': 'Save Water'},
193+
{'name': 'Shawshank', 'message': 'There is something inside'},
194+
{'name': 'Jone', 'message': 'Enough movies?'},
195+
];
196+
197+
scrollDown() {
198+
const scrollingContainerEl = this.scrollingContainer.getElementRef().nativeElement;
199+
scrollingContainerEl.scrollTop = 300;
200+
201+
// Emit a scroll event from the scrolling element in our component.
202+
dispatchFakeEvent(scrollingContainerEl, 'scroll');
203+
}
204+
205+
scrollBack() {
206+
const scrollingContainerEl = this.scrollingContainer.getElementRef().nativeElement;
207+
scrollingContainerEl.scrollTop = 0;
208+
209+
// Emit a scroll event from the scrolling element in our component.
210+
dispatchFakeEvent(scrollingContainerEl, 'scroll');
211+
}
212+
}
213+
214+
@Component({
215+
// Use styles to define the style of scrollable container and header,
216+
// which help test to make sure whether the header is stuck at the right position.
217+
styles:[`
218+
.scrollable-style {
219+
text-align: center;
220+
-webkit-appearance: none;
221+
-moz-appearance: none;
222+
height: 300px;
223+
overflow: auto;
224+
}
225+
.heading-style {
226+
background: whitesmoke;
227+
padding: 5px;
228+
}
229+
`],
230+
template: `
231+
<div cdk-scrollable class="scrollable-style">
232+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
233+
<div id="default-region">
234+
<div cdkStickyHeader class="heading-style">
235+
<h2>Heading 1</h2>
236+
</div>
237+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
238+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
239+
<p *ngFor="let item of items"> {{item.name}} : {{item.message}}</p>
240+
</div>
241+
</div>
242+
`})
243+
class StickyHeaderTestNoStickyRegion {
244+
items: any[] = [
245+
{'name': 'Forrest', 'message': 'Life was like a box of chocolates'},
246+
{'name': 'Gump', 'message': 'you never know what you are gonna get'},
247+
{'name': 'Lion King', 'message': 'Everything you see exists together'},
248+
{'name': 'Jack', 'message': 'in a delicate balance'},
249+
{'name': 'Garfield', 'message': 'Save Water'},
250+
{'name': 'Shawshank', 'message': 'There is something inside'},
251+
{'name': 'Jone', 'message': 'Enough movies?'},
252+
];
253+
}

src/lib/sticky-header/sticky-header.ts

+18-10
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {Directive, Input,
9-
OnDestroy, AfterViewInit, ElementRef, Optional} from '@angular/core';
10-
import {Platform} from '../core/platform';
9+
OnDestroy, AfterViewInit, ElementRef, Optional,
10+
InjectionToken, Injectable, Inject, Provider} from '@angular/core';
11+
import {Platform} from '../core/platform/index';
1112
import {Scrollable} from '../core/overlay/scroll/scrollable';
1213
import {extendObject} from '../core/util/object-extend';
1314
import {Subscription} from 'rxjs/Subscription';
@@ -32,7 +33,6 @@ export class CdkStickyRegion {
3233
constructor(public readonly _elementRef: ElementRef) { }
3334
}
3435

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

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

49+
export const STICKY_HEADER_SUPPORT_STRATEGY = new InjectionToken('sticky-header-support-strategy');
50+
51+
/** @docs-private
52+
* Create a factory for sticky-positioning check to make code more testable
53+
*/
54+
export const STICKY_HEADER_SUPPORT_STRATEGY_PROVIDER: Provider = {
55+
provide: STICKY_HEADER_SUPPORT_STRATEGY,
56+
useFactory: isPositionStickySupported
57+
};
58+
4959
/**
5060
* Directive that marks an element as a sticky-header. Inside of a scrolling container (marked with
5161
* cdkScrollable), this header will "stick" to the top of the scrolling viewport while its sticky
@@ -61,8 +71,6 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
6171

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

6775
/** The element with the 'cdkStickyHeader' tag. */
6876
element: HTMLElement;
@@ -97,7 +105,8 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
97105
constructor(element: ElementRef,
98106
scrollable: Scrollable,
99107
@Optional() public parentRegion: CdkStickyRegion,
100-
platform: Platform) {
108+
platform: Platform,
109+
@Inject(STICKY_HEADER_SUPPORT_STRATEGY) public _isPositionStickySupported) {
101110
if (platform.isBrowser) {
102111
this.element = element.nativeElement;
103112
this.upperScrollableContainer = scrollable.getElementRef().nativeElement;
@@ -137,7 +146,6 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
137146
* sticky positioning. If not, use the original implementation.
138147
*/
139148
private _setStrategyAccordingToCompatibility(): void {
140-
this._isPositionStickySupported = isPositionStickySupported();
141149
if (this._isPositionStickySupported) {
142150
this.element.style.top = '0';
143151
this.element.style.cssText += 'position: -webkit-sticky; position: sticky; ';
@@ -258,9 +266,9 @@ export class CdkStickyHeader implements OnDestroy, AfterViewInit {
258266

259267

260268
/**
261-
* '_applyStickyPositionStyles()' function contains the main logic of sticky-header. It decides when
262-
* a header should be stick and when should it be unstuck by comparing the offsetTop
263-
* of scrollable container with the top and bottom of the sticky region.
269+
* '_applyStickyPositionStyles()' function contains the main logic of sticky-header.
270+
* It decides when a header should be stick and when should it be unstuck by comparing
271+
* the offsetTop of scrollable container with the top and bottom of the sticky region.
264272
*/
265273
_applyStickyPositionStyles(): void {
266274
let currentPosition: number = this.upperScrollableContainer.offsetTop;

0 commit comments

Comments
 (0)