Compare commits
	
		
			20 Commits
		
	
	
		
			sim3
			...
			fix-remote
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9f7a88090c | ||
|   | 9fcebf8b11 | ||
|   | d68d7bdd8e | ||
|   | 88cbd8e40c | ||
|   | d4bce52fb0 | ||
|   | 93214c235a | ||
|   | 907da30516 | ||
|   | eb9e0d8d4d | ||
|   | eb3129bdb0 | ||
|   | d9877759d3 | ||
|   | ac21c1b70c | ||
|   | 0ea776f688 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3e3dc7dd83 | ||
|   | 50742c4f82 | ||
|   | 2f04add2a3 | ||
|   | 0ce5060246 | ||
|   | 00353cdccf | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a1ac209d74 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | bdd8477b54 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | f690f36bfb | 
| @@ -268,6 +268,9 @@ async function getCanvasPixelsWithData(page) { | ||||
|  * @param {import('@playwright/test').Page} page | ||||
|  */ | ||||
| async function assertLimitLinesExistAndAreVisible(page) { | ||||
|     // Wait for plot series data to load | ||||
|     await expect(page.locator('.js-series-data-loaded')).toBeVisible(); | ||||
|     // Wait for limit lines to be created | ||||
|     await page.waitForSelector('.js-limit-area', { state: 'attached' }); | ||||
|     const limitLineCount = await page.locator('.c-plot-limit-line').count(); | ||||
|     // There should be 10 limit lines created by default | ||||
|   | ||||
| @@ -28,6 +28,14 @@ const { test, expect } = require('../../../../pluginFixtures'); | ||||
| const { createDomainObjectWithDefaults, setRealTimeMode, setFixedTimeMode } = require('../../../../appActions'); | ||||
|  | ||||
| test.describe('Plot Tagging', () => { | ||||
|     /** | ||||
|      * Given a canvas and a set of points, tags the points on the canvas. | ||||
|      * @param {import('@playwright/test').Page} page | ||||
|      * @param {HTMLCanvasElement} canvas a telemetry item with a plot | ||||
|      * @param {Number} xEnd a telemetry item with a plot | ||||
|      * @param {Number} yEnd a telemetry item with a plot | ||||
|      * @returns {Promise} | ||||
|      */ | ||||
|     async function createTags({page, canvas, xEnd, yEnd}) { | ||||
|         await canvas.hover({trial: true}); | ||||
|  | ||||
| @@ -64,12 +72,20 @@ test.describe('Plot Tagging', () => { | ||||
|         await page.getByText('Science').click(); | ||||
|     } | ||||
|  | ||||
|     async function testTelemetryItem(page, canvas, telemetryItem) { | ||||
|     /** | ||||
|      * Given a telemetry item (e.g., a Sine Wave Generator) with a plot, tests that the plot can be tagged. | ||||
|      * @param {import('@playwright/test').Page} page | ||||
|      * @param {import('../../../../appActions').CreatedObjectInfo} telemetryItem a telemetry item with a plot | ||||
|      * @returns {Promise} | ||||
|      */ | ||||
|     async function testTelemetryItem(page, telemetryItem) { | ||||
|         // Check that telemetry item also received the tag | ||||
|         await page.goto(telemetryItem.url); | ||||
|  | ||||
|         await expect(page.getByText('No tags to display for this item')).toBeVisible(); | ||||
|  | ||||
|         const canvas = page.locator('canvas').nth(1); | ||||
|  | ||||
|         //Wait for canvas to stablize. | ||||
|         await canvas.hover({trial: true}); | ||||
|  | ||||
| @@ -85,19 +101,31 @@ test.describe('Plot Tagging', () => { | ||||
|         await expect(page.getByText('Driving')).toBeHidden(); | ||||
|     } | ||||
|  | ||||
|     async function basicTagsTests(page, canvas) { | ||||
|         // Search for Science | ||||
|     /** | ||||
|      * Given a page, tests that tags are searchable, deletable, and persist across reloads. | ||||
|      * @param {import('@playwright/test').Page} page | ||||
|      * @returns {Promise} | ||||
|      */ | ||||
|     async function basicTagsTests(page) { | ||||
|         // Search for Driving | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click(); | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc'); | ||||
|         await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText("Science"); | ||||
|         await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText("Drilling"); | ||||
|  | ||||
|         // Clicking elsewhere should cause annotation selection to be cleared | ||||
|         await expect(page.getByText('No tags to display for this item')).toBeVisible(); | ||||
|  | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv'); | ||||
|         // click on the search result | ||||
|         await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText(/Sine Wave/).first().click(); | ||||
|  | ||||
|         // Delete Driving | ||||
|         await page.hover('[aria-label="Tag"]:has-text("Driving")'); | ||||
|         await page.locator('[aria-label="Remove tag Driving"]').click(); | ||||
|  | ||||
|         await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText("Science"); | ||||
|         await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText("Driving"); | ||||
|         // Search for Science | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click(); | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc'); | ||||
|         await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText("Science"); | ||||
|         await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText("Drilling"); | ||||
|  | ||||
|         // Search for Driving | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click(); | ||||
| @@ -109,12 +137,13 @@ test.describe('Plot Tagging', () => { | ||||
|             page.reload(), | ||||
|             page.waitForLoadState('networkidle') | ||||
|         ]); | ||||
|         // wait for plot progress bar to disappear | ||||
|         await page.locator('.l-view-section.c-progress-bar').waitFor({ state: 'detached' }); | ||||
|         // wait for plots to load | ||||
|         await expect(page.locator('.js-series-data-loaded')).toBeVisible(); | ||||
|  | ||||
|         await page.getByText('Annotations').click(); | ||||
|         await expect(page.getByText('No tags to display for this item')).toBeVisible(); | ||||
|  | ||||
|         const canvas = page.locator('canvas').nth(1); | ||||
|         // click on the tagged plot point | ||||
|         await canvas.click({ | ||||
|             position: { | ||||
| @@ -171,8 +200,8 @@ test.describe('Plot Tagging', () => { | ||||
|         // changing to fixed time mode rebuilds canvas? | ||||
|         canvas = page.locator('canvas').nth(1); | ||||
|  | ||||
|         await basicTagsTests(page, canvas); | ||||
|         await testTelemetryItem(page, canvas, alphaSineWave); | ||||
|         await basicTagsTests(page); | ||||
|         await testTelemetryItem(page, alphaSineWave); | ||||
|  | ||||
|         // set to real time mode | ||||
|         await setRealTimeMode(page); | ||||
| @@ -182,8 +211,8 @@ test.describe('Plot Tagging', () => { | ||||
|         await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc'); | ||||
|         // click on the search result | ||||
|         await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText('Alpha Sine Wave').first().click(); | ||||
|         // wait for plot progress bar to disappear | ||||
|         await page.locator('.l-view-section.c-progress-bar').waitFor({ state: 'detached' }); | ||||
|         // wait for plots to load | ||||
|         await expect(page.locator('.js-series-data-loaded')).toBeVisible(); | ||||
|         // expect plot to be paused | ||||
|         await expect(page.locator('[title="Resume displaying real-time data"]')).toBeVisible(); | ||||
|  | ||||
| @@ -202,7 +231,7 @@ test.describe('Plot Tagging', () => { | ||||
|             xEnd: 700, | ||||
|             yEnd: 480 | ||||
|         }); | ||||
|         await basicTagsTests(page, canvas); | ||||
|         await basicTagsTests(page); | ||||
|     }); | ||||
|  | ||||
|     test('Tags work with Stacked Plots', async ({ page }) => { | ||||
| @@ -232,7 +261,7 @@ test.describe('Plot Tagging', () => { | ||||
|             xEnd: 700, | ||||
|             yEnd: 215 | ||||
|         }); | ||||
|         await basicTagsTests(page, canvas); | ||||
|         await testTelemetryItem(page, canvas, alphaSineWave); | ||||
|         await basicTagsTests(page); | ||||
|         await testTelemetryItem(page, alphaSineWave); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -52,12 +52,12 @@ | ||||
|     "painterro": "1.2.78", | ||||
|     "playwright-core": "1.29.0", | ||||
|     "plotly.js-basic-dist": "2.17.0", | ||||
|     "plotly.js-gl2d-dist": "2.17.1", | ||||
|     "plotly.js-gl2d-dist": "2.20.0", | ||||
|     "printj": "1.3.1", | ||||
|     "resolve-url-loader": "5.0.0", | ||||
|     "sanitize-html": "2.10.0", | ||||
|     "sass": "1.57.1", | ||||
|     "sass-loader": "13.2.0", | ||||
|     "sass": "1.59.3", | ||||
|     "sass-loader": "13.2.1", | ||||
|     "sinon": "15.0.1", | ||||
|     "style-loader": "^3.3.1", | ||||
|     "typescript": "4.9.5", | ||||
| @@ -66,7 +66,7 @@ | ||||
|     "vue-eslint-parser": "9.1.0", | ||||
|     "vue-loader": "15.9.8", | ||||
|     "vue-template-compiler": "2.6.14", | ||||
|     "webpack": "5.74.0", | ||||
|     "webpack": "5.76.2", | ||||
|     "webpack-cli": "5.0.0", | ||||
|     "webpack-dev-server": "4.11.1", | ||||
|     "webpack-merge": "5.8.0" | ||||
|   | ||||
| @@ -43,7 +43,7 @@ | ||||
|     </div> | ||||
|     <div | ||||
|         v-if="!hideOptions && filteredOptions.length > 0" | ||||
|         class="c-menu c-input--autocomplete__options" | ||||
|         class="c-menu c-input--autocomplete__options js-autocomplete-options" | ||||
|         aria-label="Autocomplete Options" | ||||
|         @blur="hideOptions = true" | ||||
|     > | ||||
|   | ||||
| @@ -322,7 +322,7 @@ export default { | ||||
|                                 rgba(125,125,125,.2) 8px | ||||
|                             )` | ||||
|                     ) : ''}`, | ||||
|                 transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`, | ||||
|                 transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX / 2}px, ${this.imageTranslateY / 2}px)`, | ||||
|                 transition: `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}`, | ||||
|                 width: `${this.sizedImageWidth}px`, | ||||
|                 height: `${this.sizedImageHeight}px` | ||||
| @@ -709,7 +709,7 @@ export default { | ||||
|         getVisibleLayerStyles(layer) { | ||||
|             return { | ||||
|                 backgroundImage: `url(${layer.source})`, | ||||
|                 transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`, | ||||
|                 transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX / 2}px, ${this.imageTranslateY / 2}px)`, | ||||
|                 transition: `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}` | ||||
|             }; | ||||
|         }, | ||||
|   | ||||
| @@ -195,7 +195,7 @@ | ||||
|         margin-bottom: 1px; | ||||
|         padding-bottom: $interiorMarginSm; | ||||
|         &.animate-scroll { | ||||
|             scroll-behavior: smooth;  | ||||
|             scroll-behavior: smooth; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -320,7 +320,7 @@ | ||||
|         flex-direction: row; | ||||
|         position: absolute; | ||||
|         left: $interiorMargin; top: $interiorMargin; | ||||
|         z-index: 70; | ||||
|         z-index: 10; | ||||
|         background: $colorLocalControlOvrBg; | ||||
|         border-radius: $basicCr; | ||||
|         align-items: center; | ||||
|   | ||||
| @@ -22,7 +22,9 @@ | ||||
| <template> | ||||
| <div | ||||
|     v-if="loaded" | ||||
|     ref="plot" | ||||
|     class="gl-plot" | ||||
|     :class="{ 'js-series-data-loaded' : seriesDataLoaded }" | ||||
| > | ||||
|     <slot></slot> | ||||
|     <div class="plot-wrapper-axis-and-display-area flex-elem grows"> | ||||
| @@ -347,6 +349,9 @@ export default { | ||||
|             const parentLeftTickWidth = this.parentYTickWidth.leftTickWidth; | ||||
|  | ||||
|             return parentLeftTickWidth || leftTickWidth; | ||||
|         }, | ||||
|         seriesDataLoaded() { | ||||
|             return ((this.pending === 0) && this.loaded); | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
| @@ -412,6 +417,7 @@ export default { | ||||
|         this.openmct.selection.off('change', this.updateSelection); | ||||
|         document.removeEventListener('keydown', this.handleKeyDown); | ||||
|         document.removeEventListener('keyup', this.handleKeyUp); | ||||
|         document.body.removeEventListener('click', this.cancelSelection); | ||||
|         this.destroy(); | ||||
|     }, | ||||
|     methods: { | ||||
| @@ -444,6 +450,19 @@ export default { | ||||
|             //This section is common to all entry points for annotation display | ||||
|             this.prepareExistingAnnotationSelection(selectedAnnotations); | ||||
|         }, | ||||
|         cancelSelection(event) { | ||||
|             if (this.$refs?.plot) { | ||||
|                 const clickedInsidePlot = this.$refs.plot.contains(event.target); | ||||
|                 const clickedInsideInspector = event.target.closest('.js-inspector') !== null; | ||||
|                 const clickedOption = event.target.closest('.js-autocomplete-options') !== null; | ||||
|                 if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) { | ||||
|                     this.rectangles = []; | ||||
|                     this.annotationSelections = []; | ||||
|                     this.selectPlot(); | ||||
|                     document.body.removeEventListener('click', this.cancelSelection); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         waitForAxesToLoad() { | ||||
|             return new Promise(resolve => { | ||||
|                 // When there is no plot data, the ranges can be undefined | ||||
| @@ -1276,6 +1295,8 @@ export default { | ||||
|             } | ||||
|  | ||||
|             this.openmct.selection.select(selection, true); | ||||
|  | ||||
|             document.body.addEventListener('click', this.cancelSelection); | ||||
|         }, | ||||
|         selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event) { | ||||
|             let targetDomainObjects = {}; | ||||
|   | ||||
| @@ -46,6 +46,7 @@ export default class RemoteClock extends DefaultClock { | ||||
|  | ||||
|         this.timeTelemetryObject = undefined; | ||||
|         this.parseTime = undefined; | ||||
|         this.formatTime = undefined; | ||||
|         this.metadata = undefined; | ||||
|  | ||||
|         this.lastTick = 0; | ||||
| @@ -137,6 +138,10 @@ export default class RemoteClock extends DefaultClock { | ||||
|         this.parseTime = (datum) => { | ||||
|             return timeFormatter.parse(datum); | ||||
|         }; | ||||
|  | ||||
|         this.formatTime = (datum) => { | ||||
|             return timeFormatter.format(datum); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -38,9 +38,8 @@ | ||||
| import {getValidatedData} from "../plan/util"; | ||||
| import ListView from '../../ui/components/List/ListView.vue'; | ||||
| import {getPreciseDuration} from "../../utils/duration"; | ||||
| import ticker from 'utils/clock/Ticker'; | ||||
| import {SORT_ORDER_OPTIONS} from "./constants"; | ||||
|  | ||||
| import _ from 'lodash'; | ||||
| import moment from "moment"; | ||||
| import { v4 as uuid } from 'uuid'; | ||||
|  | ||||
| @@ -53,16 +52,26 @@ const headerItems = [ | ||||
|         isSortable: true, | ||||
|         property: 'start', | ||||
|         name: 'Start Time', | ||||
|         format: function (value, object) { | ||||
|             return `${moment(value).format(TIME_FORMAT)}Z`; | ||||
|         format: function (value, object, key, openmct) { | ||||
|             const clock = openmct.time.clock(); | ||||
|             if (clock && clock.formatTime) { | ||||
|                 return clock.formatTime(value); | ||||
|             } else { | ||||
|                 return `${moment(value).format(TIME_FORMAT)}Z`; | ||||
|             } | ||||
|         } | ||||
|     }, { | ||||
|         defaultDirection: true, | ||||
|         isSortable: true, | ||||
|         property: 'end', | ||||
|         name: 'End Time', | ||||
|         format: function (value, object) { | ||||
|             return `${moment(value).format(TIME_FORMAT)}Z`; | ||||
|         format: function (value, object, key, openmct) { | ||||
|             const clock = openmct.time.clock(); | ||||
|             if (clock && clock.formatTime) { | ||||
|                 return clock.formatTime(value); | ||||
|             } else { | ||||
|                 return `${moment(value).format(TIME_FORMAT)}Z`; | ||||
|             } | ||||
|         } | ||||
|     }, { | ||||
|         defaultDirection: false, | ||||
| @@ -119,7 +128,7 @@ export default { | ||||
|         this.unlistenConfig = this.openmct.objects.observe(this.domainObject, 'configuration', this.setViewFromConfig); | ||||
|         this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus); | ||||
|         this.status = this.openmct.status.get(this.domainObject.identifier); | ||||
|         this.unlistenTicker = ticker.listen(this.clearPreviousActivities); | ||||
|  | ||||
|         this.openmct.time.on('bounds', this.updateTimestamp); | ||||
|         this.openmct.editor.on('isEditing', this.setEditState); | ||||
|  | ||||
| @@ -144,10 +153,6 @@ export default { | ||||
|             this.unlistenConfig(); | ||||
|         } | ||||
|  | ||||
|         if (this.unlistenTicker) { | ||||
|             this.unlistenTicker(); | ||||
|         } | ||||
|  | ||||
|         if (this.removeStatusListener) { | ||||
|             this.removeStatusListener(); | ||||
|         } | ||||
| @@ -192,8 +197,8 @@ export default { | ||||
|             } | ||||
|         }, | ||||
|         updateTimestamp(_bounds, isTick) { | ||||
|             if (isTick === true) { | ||||
|                 this.timestamp = this.openmct.time.clock().currentValue(); | ||||
|             if (isTick === true && this.openmct.time.clock() !== undefined) { | ||||
|                 this.updateTimeStampAndListActivities(this.openmct.time.clock().currentValue()); | ||||
|             } | ||||
|         }, | ||||
|         setViewFromClock(newClock) { | ||||
| @@ -202,12 +207,11 @@ export default { | ||||
|             if (isFixedTime) { | ||||
|                 this.hideAll = false; | ||||
|                 this.showAll = true; | ||||
|                 // clear invokes listActivities | ||||
|                 this.clearPreviousActivities(this.openmct.time.bounds()?.start); | ||||
|                 this.updateTimeStampAndListActivities(this.openmct.time.bounds()?.start); | ||||
|             } else { | ||||
|                 this.setSort(); | ||||
|                 this.setViewBounds(); | ||||
|                 this.listActivities(); | ||||
|                 this.updateTimeStampAndListActivities(this.openmct.time.clock().currentValue()); | ||||
|             } | ||||
|         }, | ||||
|         addItem(domainObject) { | ||||
| @@ -346,12 +350,8 @@ export default { | ||||
|             // sort by start time | ||||
|             this.planActivities = activities.sort(this.sortByStartTime); | ||||
|         }, | ||||
|         clearPreviousActivities(time) { | ||||
|             if (time instanceof Date) { | ||||
|                 this.timestamp = time.getTime(); | ||||
|             } else { | ||||
|                 this.timestamp = time; | ||||
|             } | ||||
|         updateTimeStampAndListActivities(time) { | ||||
|             this.timestamp = time; | ||||
|  | ||||
|             this.listActivities(); | ||||
|         }, | ||||
|   | ||||
| @@ -37,7 +37,7 @@ export default { | ||||
|                 // eslint-disable-next-line you-dont-need-lodash-underscore/get | ||||
|                 let value = _.get(this.item, property.key); | ||||
|                 if (property.format) { | ||||
|                     value = property.format(value, this.item, property.key); | ||||
|                     value = property.format(value, this.item, property.key, this.openmct); | ||||
|                 } | ||||
|  | ||||
|                 values.push({ | ||||
|   | ||||
| @@ -61,7 +61,7 @@ | ||||
|             pointer-events: none; | ||||
|             position: absolute; | ||||
|             top: 0; right: 0; bottom: auto; left: 0; | ||||
|             z-index: 2; | ||||
|             z-index: 10; | ||||
|  | ||||
|             .c-object-label { | ||||
|                 visibility: hidden; | ||||
| @@ -99,6 +99,8 @@ | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.c-so-view--flexible-layout, | ||||
|         &.c-so-view--layout { | ||||
|             // For sub-layouts with hidden frames, completely hide the header to avoid overlapping buttons | ||||
|             > .c-so-view__header { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| <template> | ||||
| <div class="c-inspector"> | ||||
| <div class="c-inspector js-inspector"> | ||||
|     <object-name /> | ||||
|     <InspectorTabs | ||||
|         :selection="selection" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user