Skip to content

Commit

Permalink
feat(scroll-collapse): emit affix and minimise events
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack Matthews authored and edoparearyee committed Jun 8, 2018
1 parent 00d66ae commit 0ee19b8
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 10 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
[![Commitizen friendly][commitizen-badge]][commitizen]
[![code style: prettier][prettier-badge]][prettier-badge-url]

A simple lightweight library for [Angular][angular] that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element and emits an appropriate event. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.
A simple lightweight library for [Angular][angular] that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.

Appropriate events for the above classes are also emitted.

This is a simple library for [Angular][angular], implemented in the [Angular Package Format v5.0][apfv5].

Expand Down Expand Up @@ -90,7 +92,7 @@ In this scenario the nav element will have the class `sn-affix` added when the u

```html
<header>...</header>
<nav class="foo" snScrollCollapse>
<nav class="foo" snScrollCollapse (affixChange)="affixHandler($event)">
...
</nav>
```
Expand Down Expand Up @@ -118,7 +120,7 @@ A `[yOffset]` can also be applied. Here `sn-affix` will be added when the top of
In this scenario the nav element will have the class `sn-minimise` added when the user scrolls 100px (the original height of the element) down the page.

```html
<header class="foo" snScrollCollapse>
<header class="foo" snScrollCollapse (minimiseChange)="minimiseHandler($event)">
...
</header>
```
Expand Down
8 changes: 7 additions & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<h1>Scroll down ↓</h1>

<nav class="nav" snScrollCollapse (scrollDirectionChange)="scrollDirectionHandler($event)">
<nav
class="nav"
snScrollCollapse
(scrollDirectionChange)="scrollDirectionHandler($event)"
(affixChange)="affixHandler($event)"
(minimiseChange)="minimiseHandler($event)"
>
My nav&nbsp;
<span class="down">Going down</span>
<span class="up">Going up</span>
Expand Down
8 changes: 7 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { Direction } from '..';
})
export class AppComponent {
scrollDirectionHandler(event: Direction) {
console.log(event);
console.log('scrollDirection: ', event);
}
affixHandler(event: boolean) {
console.log('affix: ', event);
}
minimiseHandler(event: boolean) {
console.log('minimise: ', event);
}
}
94 changes: 93 additions & 1 deletion src/app/scroll-collapse/scroll-collapse.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,24 +114,35 @@ describe('ScrollCollapseDirective', () => {
]);
expect(directive.isScrollingDown).toBeTruthy();
});
it('should emit scroll direction event on scroll', () => {
it('should emit scroll direction event on scroll direction change', () => {
const spy = spyOn(directive.scrollDirectionChange, 'emit');

directive.calculateScrollDirection([
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 },
{ scrollX: 0, scrollY: 100, width: 1266, height: 768 }
]);
expect(spy).toHaveBeenCalledWith(Direction.DOWN);
spy.calls.reset();

directive.calculateScrollDirection([
{ scrollX: 0, scrollY: 100, width: 1266, height: 768 },
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 }
]);
expect(spy).toHaveBeenCalledWith(Direction.UP);
spy.calls.reset();

directive.calculateScrollDirection([
{ scrollX: 0, scrollY: 50, width: 1266, height: 768 },
{ scrollX: 0, scrollY: 0, width: 1266, height: 768 }
]);
expect(spy).not.toHaveBeenCalled();
});
});

describe('minimise mode', () => {
it('should calculate minimise mode', () => {
directive.originalHeight = 100;

directive.calculateMinimiseMode({
scrollX: 0,
scrollY: 0,
Expand All @@ -157,6 +168,46 @@ describe('ScrollCollapseDirective', () => {
expect(directive.minimiseMode).toBeFalsy();
});

it('should emit minimise event on minimise mode change', () => {
const spy = spyOn(directive.minimiseChange, 'emit');
directive.originalHeight = 100;

directive.calculateMinimiseMode({
scrollX: 0,
scrollY: 200,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(true);
spy.calls.reset();

directive.calculateMinimiseMode({
scrollX: 0,
scrollY: 99,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(false);
spy.calls.reset();

directive.calculateMinimiseMode({
scrollX: 0,
scrollY: 200,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(true);
spy.calls.reset();

directive.calculateMinimiseMode({
scrollX: 0,
scrollY: 250,
width: 1366,
height: 768
});
expect(spy).not.toHaveBeenCalled();
});

it('should factor in element offsetTop when calculating minimise mode', () => {
directive.originalHeight = 100;
directive.originalTop = 100;
Expand Down Expand Up @@ -189,6 +240,7 @@ describe('ScrollCollapseDirective', () => {
describe('affix mode', () => {
it('should calculate affix mode', () => {
directive.originalTop = 100;

directive.calculateAffixMode({
scrollX: 0,
scrollY: 0,
Expand All @@ -214,6 +266,46 @@ describe('ScrollCollapseDirective', () => {
expect(directive.affixMode).toBeFalsy();
});

it('should emit affix event on affix mode change', () => {
const spy = spyOn(directive.affixChange, 'emit');
directive.originalTop = 100;

directive.calculateAffixMode({
scrollX: 0,
scrollY: 200,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(true);
spy.calls.reset();

directive.calculateAffixMode({
scrollX: 0,
scrollY: 99,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(false);
spy.calls.reset();

directive.calculateAffixMode({
scrollX: 0,
scrollY: 200,
width: 1366,
height: 768
});
expect(spy).toHaveBeenCalledWith(true);
spy.calls.reset();

directive.calculateAffixMode({
scrollX: 0,
scrollY: 250,
width: 1366,
height: 768
});
expect(spy).not.toHaveBeenCalled();
});

it('should factor in yOffset property when calculating affix mode', () => {
directive.originalHeight = 100;
directive.originalTop = 500;
Expand Down
31 changes: 27 additions & 4 deletions src/app/scroll-collapse/scroll-collapse.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,25 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
* @memberof ScrollCollapseDirective
*/
@HostBinding(classes.affixClass) public affixMode = false;
/**
* Emits affix boolean on scroll or window resize.
*
* @memberof ScrollCollapseDirective
*/
@Output() affixChange = new EventEmitter<Boolean>();
/**
* Returns true if the user has scrolled pass the origin height of
* the element assuming the element is fixed at the top of the page
*
* @memberof ScrollCollapseDirective
*/
@HostBinding(classes.minimiseClass) public minimiseMode = false;
/**
* Emits affix boolean on scroll or window resize.
*
* @memberof ScrollCollapseDirective
*/
@Output() minimiseChange = new EventEmitter<Boolean>();
/**
* Creates an instance of ScrollCollapseDirective.
* @memberof ScrollCollapseDirective
Expand Down Expand Up @@ -198,9 +210,12 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
if (noScrollChange) {
return;
}
this.scrollDirection =
const newScrollDirection =
pastEvent.scrollY > currentEvent.scrollY ? Direction.UP : Direction.DOWN;
this.scrollDirectionChange.emit(this.scrollDirection);
if (this.scrollDirection !== newScrollDirection) {
this.scrollDirection = newScrollDirection;
this.scrollDirectionChange.emit(this.scrollDirection);
}
}
/**
* Calculate if the user has scrolled pass the origin height of
Expand All @@ -209,8 +224,12 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
* @memberof ScrollCollapseDirective
*/
public calculateMinimiseMode(viewport: Viewport): void {
this.minimiseMode =
const newMinimiseMode =
viewport.scrollY >= this.originalHeight + this.originalTop;
if (this.minimiseMode !== newMinimiseMode) {
this.minimiseMode = newMinimiseMode;
this.minimiseChange.emit(this.minimiseMode);
}
}
/**
* Calculate if the user has scrolled pass the origin height of
Expand All @@ -219,7 +238,11 @@ export class ScrollCollapseDirective implements AfterViewInit, OnDestroy {
* @memberof ScrollCollapseDirective
*/
public calculateAffixMode(viewport: Viewport): void {
this.affixMode = viewport.scrollY >= this.originalTop - this.yOffset;
const newAffixMode = viewport.scrollY >= this.originalTop - this.yOffset;
if (this.affixMode !== newAffixMode) {
this.affixMode = newAffixMode;
this.affixChange.emit(this.affixMode);
}
}
/**
* Return current viewport values
Expand Down

0 comments on commit 0ee19b8

Please sign in to comment.