From 1c4a3ca20f700a72a4971aabc64db36bbfefa5ed Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Sun, 27 Dec 2015 01:02:17 +0200 Subject: [PATCH] make scrollYOffset option consume selector and function --- demo/index.html | 2 +- .../StickySidebar/sticky-sidebar.js | 5 +- .../StickySidebar/sticky-sidebar.spec.js | 2 +- lib/components/Redoc/redoc.js | 28 +++++++- lib/components/Redoc/redoc.spec.js | 65 ++++++++++++++++--- lib/components/SideMenu/side-menu.js | 6 +- lib/components/SideMenu/side-menu.spec.js | 2 +- lib/index.js | 4 -- lib/options.js | 5 ++ 9 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 lib/options.js diff --git a/demo/index.html b/demo/index.html index ba33766e..f1b97272 100644 --- a/demo/index.html +++ b/demo/index.html @@ -12,7 +12,7 @@ - + Loading... diff --git a/lib/common/components/StickySidebar/sticky-sidebar.js b/lib/common/components/StickySidebar/sticky-sidebar.js index 17eac49b..76637c03 100644 --- a/lib/common/components/StickySidebar/sticky-sidebar.js +++ b/lib/common/components/StickySidebar/sticky-sidebar.js @@ -37,7 +37,7 @@ export default class StickySidebar { } updatePosition() { - if ( this.scrollY + this.scrollYOffset >= this.redocEl.offsetTop) { + if ( this.scrollY + this.scrollYOffset() >= this.redocEl.offsetTop) { this.stick(); } else { this.unstick(); @@ -46,7 +46,7 @@ export default class StickySidebar { stick() { this.adapter.setStyle(this.element, 'position', 'fixed'); - this.adapter.setStyle(this.element, 'top', this.scrollYOffset); + this.adapter.setStyle(this.element, 'top', this.scrollYOffset()); } unstick() { @@ -60,7 +60,6 @@ export default class StickySidebar { ngOnInit() { this.redocEl = this.element.offsetParent; - this.scrollYOffset = parseInt(this.scrollYOffset); this.bind(); } diff --git a/lib/common/components/StickySidebar/sticky-sidebar.spec.js b/lib/common/components/StickySidebar/sticky-sidebar.spec.js index 9eb4e1a2..266f23a2 100644 --- a/lib/common/components/StickySidebar/sticky-sidebar.spec.js +++ b/lib/common/components/StickySidebar/sticky-sidebar.spec.js @@ -75,6 +75,6 @@ class TestApp { constructor() { this.options = {}; this.scrollParent = window; - this.options.scrollYOffset = 20; + this.options.scrollYOffset = () => 20; } } diff --git a/lib/components/Redoc/redoc.js b/lib/components/Redoc/redoc.js index eb6e31f2..ac90a6b5 100644 --- a/lib/components/Redoc/redoc.js +++ b/lib/components/Redoc/redoc.js @@ -8,13 +8,15 @@ import ApiLogo from '../ApiLogo/api-logo'; import MethodsList from '../MethodsList/methods-list'; import SideMenu from '../SideMenu/side-menu'; import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar'; -import {options as defaultOptions} from '../../index'; +import {options as defaultOptions} from '../../options'; import {ChangeDetectionStrategy} from 'angular2/core'; import {ElementRef} from 'angular2/core'; import {BrowserDomAdapter} from 'angular2/platform/browser'; import detectScollParent from 'scrollparent'; +import {isFunction} from 'angular2/src/facade/lang'; + let optionNames = new Set(['scrollYOffset']); @RedocComponent({ @@ -37,6 +39,7 @@ export default class Redoc extends BaseComponent { this.scrollParent = detectScollParent(el); this.parseOptions(); this.options = Object.assign({}, defaultOptions, this.options); + this.normalizeOptions(); } parseOptions() { @@ -53,9 +56,28 @@ export default class Redoc extends BaseComponent { .forEach(option => { this.options[option.name] = attributesMap.get(option.attrName); }); + } - // post-process options - this.options.scrollYOffset = parseInt(this.options.scrollYOffset); + normalizeOptions() { + // modify scrollYOffset to always be a function + if (!isFunction(this.options.scrollYOffset)) { + if (isFinite(this.options.scrollYOffset)) { + // if number specified create function that returns this value + let numberOffset = parseFloat(this.options.scrollYOffset); + this.options.scrollYOffset = () => numberOffset; + } else { + // if selector or node function that returns bottom offset of this node + let el = this.options.scrollYOffset; + if (!(el instanceof Node)) { + el = this.dom.query(el); + } + if (!el) { + this.options.scrollYOffset = () => 0; + } else { + this.options.scrollYOffset = () => el.offsetTop + el.offsetHeight; + } + } + } } } Redoc.parameters = Redoc.parameters.concat([[ElementRef], [BrowserDomAdapter]]); diff --git a/lib/components/Redoc/redoc.spec.js b/lib/components/Redoc/redoc.spec.js index 5023a8e4..4a939245 100644 --- a/lib/components/Redoc/redoc.spec.js +++ b/lib/components/Redoc/redoc.spec.js @@ -1,7 +1,7 @@ 'use strict'; import { getChildDebugElement } from 'tests/helpers'; -import {Component, View, provide} from 'angular2/core'; +import {Component, View, ViewMetadata, provide} from 'angular2/core'; import {BrowserDomAdapter} from 'angular2/platform/browser'; import { @@ -14,6 +14,7 @@ import { import Redoc from 'lib/components/Redoc/redoc'; import SchemaManager from 'lib/utils/SchemaManager'; +import {options} from 'lib/options'; describe('Redoc components', () => { describe('Redoc Component', () => { @@ -45,14 +46,60 @@ describe('Redoc components', () => { }, err => done.fail(err)); }); - it('should parse component options from host element', (done) => { - builder.createAsync(TestApp).then(fixture => { - let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance; - fixture.detectChanges(); - component.options.scrollYOffset.should.be.equal(50); + describe('Options', () => { + let component; + let fixture; + + function build(tmpl, cb) { + builder = builder.overrideView(TestApp, + new ViewMetadata({template: tmpl, directives: [Redoc]})); + builder.createAsync(TestApp).then(_fixture => { + fixture = _fixture; + component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance; + fixture.detectChanges(); + cb(); + }, err => cb(err)); + } + + afterEach(() => { fixture.destroy(); - done(); - }, err => done.fail(err)); + }); + + it('should parse numeric scrollYOffset', (done) => { + build(``, err => { + if (err) return done.fail(err); + component.options.scrollYOffset().should.be.equal(50); + done(); + }); + }); + + it('should parse selector scrollYOffset', (done) => { + build(`
+ `, err => { + if (err) return done.fail(err); + component.options.scrollYOffset().should.be.equal(50); + done(); + }); + }); + + it('should return 0 for incorrect selector scrollYOffset', (done) => { + build(`
+ `, err => { + if (err) return done.fail(err); + component.options.scrollYOffset().should.be.equal(0); + done(); + }); + }); + + it('should handle function scrollYOffset', (done) => { + options.scrollYOffset = () => 123; + build(``, err => { + if (err) return done.fail(err); + component.options.scrollYOffset().should.be.equal(123); + options.scrollYOffset = 0; + done(); + }); + }); }); }); }); @@ -62,7 +109,7 @@ describe('Redoc components', () => { @View({ directives: [Redoc], template: - `` + `` }) class TestApp { } diff --git a/lib/components/SideMenu/side-menu.js b/lib/components/SideMenu/side-menu.js index 5e9c5c69..740f4d8d 100644 --- a/lib/components/SideMenu/side-menu.js +++ b/lib/components/SideMenu/side-menu.js @@ -84,7 +84,7 @@ export default class SideMenu extends BaseComponent { scrollTo(el) { // TODO: rewrite this to use offsetTop as more reliable solution let subjRect = el.getBoundingClientRect(); - let offset = this.scrollY() + subjRect.top - this.scrollYOffset + 1; + let offset = this.scrollY() + subjRect.top - this.scrollYOffset() + 1; if (this.scrollParent.scrollTo) { this.scrollParent.scrollTo(0, offset); } else { @@ -162,11 +162,11 @@ export default class SideMenu extends BaseComponent { /* returns 1 if element if above the view, 0 if in view and -1 below the view */ getElementInViewPos(el) { - if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset) { + if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset()) { return INVIEW_POSITION.ABOVE; } - if (el.getBoundingClientRect().bottom <= this.scrollYOffset) { + if (el.getBoundingClientRect().bottom <= this.scrollYOffset()) { return INVIEW_POSITION.BELLOW; } return INVIEW_POSITION.INVIEW; diff --git a/lib/components/SideMenu/side-menu.spec.js b/lib/components/SideMenu/side-menu.spec.js index 8309145f..975f4b46 100644 --- a/lib/components/SideMenu/side-menu.spec.js +++ b/lib/components/SideMenu/side-menu.spec.js @@ -20,7 +20,7 @@ import SchemaManager from 'lib/utils/SchemaManager'; let _mockRedoc = { options: { - scrollYOffset: 0 + scrollYOffset: () => 0 }, scrollParent: window }; diff --git a/lib/index.js b/lib/index.js index 30b87de5..93854637 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,10 +6,6 @@ import SchemaManager from './utils/SchemaManager'; import {redocEvents} from './events'; export * from './components/index'; -export var options = { - scrollYOffset: 0 -}; - export function init(schemaUrl) { var promise = new Promise(function(resolve, reject) { diff --git a/lib/options.js b/lib/options.js new file mode 100644 index 00000000..be544497 --- /dev/null +++ b/lib/options.js @@ -0,0 +1,5 @@ +'use strict'; + +export var options = { + scrollYOffset: 0 +};