diff --git a/src/components/Select.vue b/src/components/Select.vue index 569f4d8..4cfa605 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -119,8 +119,8 @@ @@ -478,25 +476,52 @@ }, maybeAdjustScrollPosition() { - let pointerHeight = this.$els.dropdownMenu.children[this.typeAheadPointer].offsetHeight - let pixelsToPointerTop = 0 + let bounds = this.viewport() + let pixelsToPointerTop = this.pixelsToPointerTop() + let pixelsToPointerBottom = this.pixelsToPointerBottom() + let pointerHeight = this.pointerHeight() - for ( let i = 0; i < this.typeAheadPointer; i++ ) { + if (pixelsToPointerTop <= bounds.top) { + return this.scrollTo(pixelsToPointerTop) + } else if (pixelsToPointerBottom >= bounds.bottom) { +// return this.scrollTo(( this.pixelsToPointerCenter() - this.$els.dropdownMenu.offsetHeight ) + ( pointerHeight / 2)) + return this.scrollTo(( this.pixelsToPointerCenter() - this.$els.dropdownMenu.offsetHeight ) + pointerHeight) + } + }, + + pixelsToPointerTop() { + let pixelsToPointerTop = 0 + for (let i = 0; i < this.typeAheadPointer; i++) { pixelsToPointerTop += this.$els.dropdownMenu.children[i].offsetHeight } + return pixelsToPointerTop + }, - let pixelsToPointerBottom = pixelsToPointerTop + pointerHeight + pixelsToPointerBottom() { + return this.pixelsToPointerTop() + this.pointerHeight() + }, - let bounds = { - top: this.$els.dropdownWrapper.scrollTop, - bottom: this.$els.dropdownWrapper.offsetHeight + this.$els.dropdownWrapper.scrollTop + pixelsToPointerCenter() { + return this.pixelsToPointerTop() + ( this.pointerHeight() / 2 ) + }, + + pointerHeight() { + return this.$els.dropdownMenu.children[this.typeAheadPointer].offsetHeight + }, + + /** + * The viewport is the currently viewable + * portion of the dropdown menu + */ + viewport() { + return { + top: this.$els.dropdownMenu.scrollTop, + bottom: this.$els.dropdownMenu.offsetHeight + this.$els.dropdownMenu.scrollTop } + }, - if( pixelsToPointerTop <= bounds.top ) { - this.$els.dropdownWrapper.scrollTop = pixelsToPointerTop - } else if ( pixelsToPointerBottom >= bounds.bottom) { - this.$els.dropdownWrapper.scrollTop = ( pixelsToPointerBottom - this.$els.dropdownWrapper.offsetHeight ) + pointerHeight - } + scrollTo(position) { + return this.$els.dropdownMenu.scrollTop = position }, /** @@ -547,10 +572,6 @@ computed: { - scrollTop() { - return [this.$els.dropdownMenu.scrollTop,this.$el.scrollTop] - }, - /** * Classes to be output on .dropdown * @return {Object} diff --git a/test/unit/specs/Select.spec.js b/test/unit/specs/Select.spec.js index 79787a3..c7bb75c 100644 --- a/test/unit/specs/Select.spec.js +++ b/test/unit/specs/Select.spec.js @@ -365,6 +365,79 @@ describe('Select.vue', () => { vm.$children[0].typeAheadDown() expect(vm.$children[0].typeAheadPointer).toEqual(2) }) + + it('should check if the scroll position needs to be adjusted on up arrow keyup', () => { + const vm = new Vue({ + template: '
', + components: {vSelect}, + data: { + options: ['one', 'two', 'three'] + } + }).$mount() + + vm.$children[0].typeAheadPointer = 1 + spyOn(vm.$children[0], 'maybeAdjustScrollPosition') + trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 38) + expect(vm.$children[0].maybeAdjustScrollPosition).toHaveBeenCalled() + }) + + it('should check if the scroll position needs to be adjusted on down arrow keyup', () => { + const vm = new Vue({ + template: '
', + components: {vSelect}, + data: { + options: ['one', 'two', 'three'] + } + }).$mount() + + spyOn(vm.$children[0], 'maybeAdjustScrollPosition') + + vm.$children[0].$els.search.focus() + + trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 40) + expect(vm.$children[0].maybeAdjustScrollPosition).toHaveBeenCalled() + }) + + it('should scroll up if the new pointer is above the current viewport bounds', () => { + + + }) + + describe('Measuring pixel distances', () => { + it('should calculate pixelsToPointerTop as the sum of the height all options above the pointer', () => { + const vm = new Vue({ + template: '
', + components: {vSelect}, + data: { + options: ['one', 'two', 'three'] + } + }).$mount() + + vm.$children[0].typeAheadPointer = 1 + let lineHeight = vm.$children[0].$els.dropdownMenu.children[0].offsetHeight + expect(vm.$children[0].pixelsToPointerTop()).toEqual( lineHeight * 2 ) + }) + it('should calculate pixelsToPointerBottom as the sum of the height all options above the pointer plus the height of the pointer', () => { + const vm = new Vue({ + template: '
', + components: {vSelect}, + data: { + options: ['one', 'two', 'three'] + } + }).$mount() + + vm.$children[0].typeAheadPointer = 1 + let lineHeight = vm.$children[0].$els.dropdownMenu.children[0].offsetHeight + console.log(lineHeight) + expect(vm.$children[0].pixelsToPointerTop()).toEqual( lineHeight * 10 + lineHeight) + }) + it('should calculate pixelsToPointerCenter', () => { + + }) + it('should calculate pointerHeight', () => { + + }) + }) }) describe('Removing values', () => { @@ -611,4 +684,4 @@ describe('Select.vue', () => { }) // also see example testing a component with mocks at -// https://github.com/vuejs/vueify-example/blob/master/test/unit/a.spec.js#L22-L43 +// http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html