From 57e02db6b53d986245619a830d9e220b6a172a16 Mon Sep 17 00:00:00 2001 From: Michael Rogers Date: Fri, 16 Dec 2022 03:06:16 -0600 Subject: [PATCH] Prevent scrolling the window area on new image thumb telemetry - 5867 (#5961) * Setup a scroll handler to avoid using scrollIntoView when in a layout * Implement a separate scroll to action when in layouts * Simplified scroll reset event and logic * Adjust test to capture new scroll handler * Remove done invocation after converting to async fn * Prevent default for arrow keys to avoid scrolling layoyut * await scrollToFocused * Logical or to nullish coalescing * Removed set in favor of using isNavigatedObject api * Apply animation style after image history has length * Lint fixes Co-authored-by: Scott Bell --- .../imagery/components/ImageryView.vue | 93 +++++++++++++------ .../imagery/components/imagery-view.scss | 3 + src/plugins/imagery/pluginSpec.js | 22 ++--- 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/src/plugins/imagery/components/ImageryView.vue b/src/plugins/imagery/components/ImageryView.vue index 84942c1ee9..4eb987cc73 100644 --- a/src/plugins/imagery/components/ImageryView.vue +++ b/src/plugins/imagery/components/ImageryView.vue @@ -25,7 +25,7 @@ tabindex="0" class="c-imagery" @keyup="arrowUpHandler" - @keydown="arrowDownHandler" + @keydown.prevent="arrowDownHandler" @mouseover="focusElement" >
@@ -165,6 +165,9 @@
@@ -192,6 +195,7 @@ import eventHelpers from '../lib/eventHelpers'; import _ from 'lodash'; import moment from 'moment'; +import Vue from 'vue'; import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry'; import Compass from './Compass/Compass.vue'; @@ -289,7 +293,8 @@ export default { pan: undefined, animateZoom: true, imagePanned: false, - forceShowThumbnails: false + forceShowThumbnails: false, + animateThumbScroll: false }; }, computed: { @@ -393,6 +398,12 @@ export default { return disabled; }, + isComposedInLayout() { + return ( + this.currentView?.objectPath + && !this.openmct.router.isNavigatedObject(this.currentView.objectPath) + ); + }, focusedImage() { return this.imageHistory[this.focusedImageIndex]; }, @@ -542,7 +553,7 @@ export default { }, watch: { imageHistory: { - handler(newHistory, _oldHistory) { + async handler(newHistory, oldHistory) { const newSize = newHistory.length; let imageIndex = newSize > 0 ? newSize - 1 : undefined; if (this.focusedImageTimestamp !== undefined) { @@ -570,10 +581,13 @@ export default { if (!this.isPaused) { this.setFocusedImage(imageIndex); - this.scrollToRight(); - } else { - this.scrollToFocused(); } + + await this.scrollHandler(); + if (oldHistory?.length > 0) { + this.animateThumbScroll = true; + } + }, deep: true }, @@ -584,7 +598,7 @@ export default { this.getImageNaturalDimensions(); }, bounds() { - this.scrollToFocused(); + this.scrollHandler(); }, isFixed(newValue) { const isRealTime = !newValue; @@ -848,6 +862,13 @@ export default { const disableScroll = scrollWidth > Math.ceil(scrollLeft + clientWidth); this.autoScroll = !disableScroll; }, + handlePauseButton(newState) { + this.paused(newState); + if (newState) { + // need to set the focused index or the paused focus will drift + this.thumbnailClicked(this.focusedImageIndex); + } + }, paused(state) { this.isPaused = Boolean(state); @@ -855,7 +876,7 @@ export default { this.previousFocusedImage = null; this.setFocusedImage(this.nextImageIndex); this.autoScroll = true; - this.scrollToRight(); + this.scrollHandler(); } }, scrollToFocused() { @@ -865,28 +886,43 @@ export default { } let domThumb = thumbsWrapper.children[this.focusedImageIndex]; - - if (domThumb) { - domThumb.scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center' - }); - } - }, - scrollToRight(type) { - if (type !== 'reset' && (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll)) { + if (!domThumb) { return; } - const scrollWidth = this.$refs.thumbsWrapper.scrollWidth || 0; + // separate scrollTo function had to be implemented since scrollIntoView + // caused undesirable behavior in layouts + // and could not simply be scoped to the parent element + if (this.isComposedInLayout) { + const wrapperWidth = this.$refs.thumbsWrapper.clientWidth ?? 0; + this.$refs.thumbsWrapper.scrollLeft = ( + domThumb.offsetLeft - (wrapperWidth - domThumb.clientWidth) / 2); + + return; + } + + domThumb.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + }, + async scrollToRight() { + + const scrollWidth = this.$refs?.thumbsWrapper?.scrollWidth ?? 0; if (!scrollWidth) { return; } - this.$nextTick(() => { - this.$refs.thumbsWrapper.scrollLeft = scrollWidth; - }); + await Vue.nextTick(); + this.$refs.thumbsWrapper.scrollLeft = scrollWidth; + }, + scrollHandler() { + if (this.isPaused) { + return this.scrollToFocused(); + } else if (this.autoScroll) { + return this.scrollToRight(); + } }, matchIndexOfPreviousImage(previous, imageHistory) { // match logic uses a composite of url and time to account @@ -1087,7 +1123,7 @@ export default { this.setSizedImageDimensions(); this.setImageViewport(); this.calculateViewHeight(); - this.scrollToFocused(); + this.scrollHandler(); }, setSizedImageDimensions() { this.focusedImageNaturalAspectRatio = this.$refs.focusedImage.naturalWidth / this.$refs.focusedImage.naturalHeight; @@ -1122,9 +1158,7 @@ export default { this.handleThumbWindowResizeEnded(); }, handleThumbWindowResizeEnded() { - if (!this.isPaused) { - this.scrollToRight('reset'); - } + this.scrollHandler(); this.calculateViewHeight(); @@ -1137,7 +1171,6 @@ export default { }, wheelZoom(e) { e.preventDefault(); - this.$refs.imageControls.wheelZoom(e); }, startPan(e) { diff --git a/src/plugins/imagery/components/imagery-view.scss b/src/plugins/imagery/components/imagery-view.scss index b90234f9e7..ee0713244b 100644 --- a/src/plugins/imagery/components/imagery-view.scss +++ b/src/plugins/imagery/components/imagery-view.scss @@ -194,6 +194,9 @@ overflow-y: hidden; margin-bottom: 1px; padding-bottom: $interiorMarginSm; + &.animate-scroll { + scroll-behavior: smooth; + } } &__auto-scroll-resume-button { diff --git a/src/plugins/imagery/pluginSpec.js b/src/plugins/imagery/pluginSpec.js index 634639160e..15a81b09e3 100644 --- a/src/plugins/imagery/pluginSpec.js +++ b/src/plugins/imagery/pluginSpec.js @@ -481,19 +481,16 @@ describe("The Imagery View Layouts", () => { }); }); }); - it ('scrollToRight is called when clicking on auto scroll button', (done) => { - Vue.nextTick(() => { - // use spyon to spy the scroll function - spyOn(imageryView._getInstance().$refs.ImageryContainer, 'scrollToRight'); - imageryView._getInstance().$refs.ImageryContainer.autoScroll = false; - Vue.nextTick(() => { - parent.querySelector('.c-imagery__auto-scroll-resume-button').click(); - expect(imageryView._getInstance().$refs.ImageryContainer.scrollToRight).toHaveBeenCalledWith('reset'); - done(); - }); - }); + it ('scrollToRight is called when clicking on auto scroll button', async () => { + await Vue.nextTick(); + // use spyon to spy the scroll function + spyOn(imageryView._getInstance().$refs.ImageryContainer, 'scrollHandler'); + imageryView._getInstance().$refs.ImageryContainer.autoScroll = false; + await Vue.nextTick(); + parent.querySelector('.c-imagery__auto-scroll-resume-button').click(); + expect(imageryView._getInstance().$refs.ImageryContainer.scrollHandler); }); - xit('should change the image zoom factor when using the zoom buttons', async (done) => { + xit('should change the image zoom factor when using the zoom buttons', async () => { await Vue.nextTick(); let imageSizeBefore; let imageSizeAfter; @@ -512,7 +509,6 @@ describe("The Imagery View Layouts", () => { imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect(); expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height); expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width); - done(); }); xit('should reset the zoom factor on the image when clicking the zoom button', async (done) => { await Vue.nextTick();