mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-10-30 23:37:28 +03:00 
			
		
		
		
	make scrollYOffset option consume selector and function
This commit is contained in:
		
							parent
							
								
									db8f9e8a96
								
							
						
					
					
						commit
						1c4a3ca20f
					
				|  | @ -12,7 +12,7 @@ | ||||||
|     </nav> |     </nav> | ||||||
|     </nav> |     </nav> | ||||||
| 
 | 
 | ||||||
|     <redoc scroll-y-offset="50"> |     <redoc scroll-y-offset="body > nav"> | ||||||
|       Loading... |       Loading... | ||||||
|     </redoc> |     </redoc> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ export default class StickySidebar { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   updatePosition() { |   updatePosition() { | ||||||
|     if ( this.scrollY + this.scrollYOffset >= this.redocEl.offsetTop) { |     if ( this.scrollY + this.scrollYOffset() >= this.redocEl.offsetTop) { | ||||||
|       this.stick(); |       this.stick(); | ||||||
|     } else { |     } else { | ||||||
|       this.unstick(); |       this.unstick(); | ||||||
|  | @ -46,7 +46,7 @@ export default class StickySidebar { | ||||||
| 
 | 
 | ||||||
|   stick() { |   stick() { | ||||||
|     this.adapter.setStyle(this.element, 'position', 'fixed'); |     this.adapter.setStyle(this.element, 'position', 'fixed'); | ||||||
|     this.adapter.setStyle(this.element, 'top', this.scrollYOffset); |     this.adapter.setStyle(this.element, 'top', this.scrollYOffset()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   unstick() { |   unstick() { | ||||||
|  | @ -60,7 +60,6 @@ export default class StickySidebar { | ||||||
| 
 | 
 | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.redocEl = this.element.offsetParent; |     this.redocEl = this.element.offsetParent; | ||||||
|     this.scrollYOffset = parseInt(this.scrollYOffset); |  | ||||||
|     this.bind(); |     this.bind(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,6 +75,6 @@ class TestApp { | ||||||
|   constructor() { |   constructor() { | ||||||
|     this.options = {}; |     this.options = {}; | ||||||
|     this.scrollParent = window; |     this.scrollParent = window; | ||||||
|     this.options.scrollYOffset = 20; |     this.options.scrollYOffset = () => 20; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,13 +8,15 @@ import ApiLogo from '../ApiLogo/api-logo'; | ||||||
| import MethodsList from '../MethodsList/methods-list'; | import MethodsList from '../MethodsList/methods-list'; | ||||||
| import SideMenu from '../SideMenu/side-menu'; | import SideMenu from '../SideMenu/side-menu'; | ||||||
| import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar'; | 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 {ChangeDetectionStrategy} from 'angular2/core'; | ||||||
| import {ElementRef} from 'angular2/core'; | import {ElementRef} from 'angular2/core'; | ||||||
| import {BrowserDomAdapter} from 'angular2/platform/browser'; | import {BrowserDomAdapter} from 'angular2/platform/browser'; | ||||||
| import detectScollParent from 'scrollparent'; | import detectScollParent from 'scrollparent'; | ||||||
| 
 | 
 | ||||||
|  | import {isFunction} from 'angular2/src/facade/lang'; | ||||||
|  | 
 | ||||||
| let optionNames = new Set(['scrollYOffset']); | let optionNames = new Set(['scrollYOffset']); | ||||||
| 
 | 
 | ||||||
| @RedocComponent({ | @RedocComponent({ | ||||||
|  | @ -37,6 +39,7 @@ export default class Redoc extends BaseComponent { | ||||||
|     this.scrollParent = detectScollParent(el); |     this.scrollParent = detectScollParent(el); | ||||||
|     this.parseOptions(); |     this.parseOptions(); | ||||||
|     this.options = Object.assign({}, defaultOptions, this.options); |     this.options = Object.assign({}, defaultOptions, this.options); | ||||||
|  |     this.normalizeOptions(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   parseOptions() { |   parseOptions() { | ||||||
|  | @ -53,9 +56,28 @@ export default class Redoc extends BaseComponent { | ||||||
|       .forEach(option => { |       .forEach(option => { | ||||||
|         this.options[option.name] = attributesMap.get(option.attrName); |         this.options[option.name] = attributesMap.get(option.attrName); | ||||||
|       }); |       }); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     // post-process options
 |   normalizeOptions() { | ||||||
|     this.options.scrollYOffset = parseInt(this.options.scrollYOffset); |     // 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]]); | Redoc.parameters = Redoc.parameters.concat([[ElementRef], [BrowserDomAdapter]]); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| import { getChildDebugElement } from 'tests/helpers'; | 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 {BrowserDomAdapter} from 'angular2/platform/browser'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|  | @ -14,6 +14,7 @@ import { | ||||||
| 
 | 
 | ||||||
| import Redoc from 'lib/components/Redoc/redoc'; | import Redoc from 'lib/components/Redoc/redoc'; | ||||||
| import SchemaManager from 'lib/utils/SchemaManager'; | import SchemaManager from 'lib/utils/SchemaManager'; | ||||||
|  | import {options} from 'lib/options'; | ||||||
| 
 | 
 | ||||||
| describe('Redoc components', () => { | describe('Redoc components', () => { | ||||||
|   describe('Redoc Component', () => { |   describe('Redoc Component', () => { | ||||||
|  | @ -45,14 +46,60 @@ describe('Redoc components', () => { | ||||||
|       }, err => done.fail(err)); |       }, err => done.fail(err)); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should parse component options from host element', (done) => { |     describe('Options', () => { | ||||||
|       builder.createAsync(TestApp).then(fixture => { |       let component; | ||||||
|         let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance; |       let fixture; | ||||||
|         fixture.detectChanges(); | 
 | ||||||
|         component.options.scrollYOffset.should.be.equal(50); |       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(); |         fixture.destroy(); | ||||||
|         done(); |       }); | ||||||
|       }, err => done.fail(err)); | 
 | ||||||
|  |       it('should parse numeric scrollYOffset', (done) => { | ||||||
|  |         build(`<redoc scroll-y-offset="50"></redoc>`, err => { | ||||||
|  |           if (err) return done.fail(err); | ||||||
|  |           component.options.scrollYOffset().should.be.equal(50); | ||||||
|  |           done(); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should parse selector scrollYOffset', (done) => { | ||||||
|  |         build(`<div id="test" style="position: fixed; height: 50px; top:0"> </div>
 | ||||||
|  |               <redoc scroll-y-offset="#test"></redoc>`, 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(`<div id="test" style="position: fixed; height: 50px; top:0"> </div>
 | ||||||
|  |               <redoc scroll-y-offset="#test2"></redoc>`, 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(`<redoc></redoc>`, 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({ | @View({ | ||||||
|   directives: [Redoc], |   directives: [Redoc], | ||||||
|   template: |   template: | ||||||
|       `<redoc scroll-y-offset="50"></redoc>` |       `<redoc></redoc>` | ||||||
| }) | }) | ||||||
| class TestApp { | class TestApp { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -84,7 +84,7 @@ export default class SideMenu extends BaseComponent { | ||||||
|   scrollTo(el) { |   scrollTo(el) { | ||||||
|     // TODO: rewrite this to use offsetTop as more reliable solution
 |     // TODO: rewrite this to use offsetTop as more reliable solution
 | ||||||
|     let subjRect = el.getBoundingClientRect(); |     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) { |     if (this.scrollParent.scrollTo) { | ||||||
|       this.scrollParent.scrollTo(0, offset); |       this.scrollParent.scrollTo(0, offset); | ||||||
|     } else { |     } 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 */ |   /* returns 1 if element if above the view, 0 if in view and -1 below the view */ | ||||||
|   getElementInViewPos(el) { |   getElementInViewPos(el) { | ||||||
|     if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset) { |     if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset()) { | ||||||
|       return INVIEW_POSITION.ABOVE; |       return INVIEW_POSITION.ABOVE; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (el.getBoundingClientRect().bottom <= this.scrollYOffset) { |     if (el.getBoundingClientRect().bottom <= this.scrollYOffset()) { | ||||||
|       return INVIEW_POSITION.BELLOW; |       return INVIEW_POSITION.BELLOW; | ||||||
|     } |     } | ||||||
|     return INVIEW_POSITION.INVIEW; |     return INVIEW_POSITION.INVIEW; | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ import SchemaManager from 'lib/utils/SchemaManager'; | ||||||
| 
 | 
 | ||||||
| let _mockRedoc = { | let _mockRedoc = { | ||||||
|   options: { |   options: { | ||||||
|     scrollYOffset: 0 |     scrollYOffset: () => 0 | ||||||
|   }, |   }, | ||||||
|   scrollParent: window |   scrollParent: window | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -6,10 +6,6 @@ import SchemaManager from './utils/SchemaManager'; | ||||||
| import {redocEvents} from './events'; | import {redocEvents} from './events'; | ||||||
| export * from  './components/index'; | export * from  './components/index'; | ||||||
| 
 | 
 | ||||||
| export var options = { |  | ||||||
|   scrollYOffset: 0 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export function init(schemaUrl) { | export function init(schemaUrl) { | ||||||
|   var promise = new Promise(function(resolve, reject) { |   var promise = new Promise(function(resolve, reject) { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								lib/options.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/options.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | export var options = { | ||||||
|  |   scrollYOffset: 0 | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user