Compare commits

..

17 Commits

Author SHA1 Message Date
unlikelyzero
521202f492 flip playwright install 2022-03-30 16:52:46 -07:00
unlikelyzero
6955801834 Merge branch 'fix-github-actions' of https://github.com/nasa/openmct into fix-github-actions 2022-03-30 16:51:42 -07:00
Joe Pea
872d0d2e06 Merge branch 'master' into fix-github-actions 2022-03-30 15:19:21 -07:00
John Hill
f7effe8964 restrict to label events 2022-03-30 13:33:47 -07:00
John Hill
c94daac241 Merge branch 'master' into fix-github-actions 2022-03-30 13:28:31 -07:00
unlikelyzero
35e29e73ed add comment 2022-03-30 12:33:55 -07:00
unlikelyzero
ad44cd8062 pin versions again 2022-03-30 12:28:25 -07:00
unlikelyzero
ae553e2b82 reset based on doc 2022-03-30 12:10:36 -07:00
unlikelyzero
bc6856f0f1 fix build error 2022-03-30 12:08:04 -07:00
unlikelyzero
8eba1a83a3 Merge branch 'fix-github-actions' of https://github.com/nasa/openmct into fix-github-actions 2022-03-30 12:06:16 -07:00
unlikelyzero
1b187241dc add triggers for opened PRs 2022-03-30 12:06:10 -07:00
John Hill
3f6152c6ff Merge branch 'master' into fix-github-actions 2022-03-30 11:44:16 -07:00
unlikelyzero
4e32637469 check latest 2022-03-30 11:37:47 -07:00
unlikelyzero
722690ca15 Add node testing and explicit ubuntu version 2022-03-30 11:37:38 -07:00
unlikelyzero
eb7da39ceb add caching and check latest 2022-03-30 11:37:24 -07:00
unlikelyzero
8896a0dcfa remove broken functionaliyt 2022-03-30 11:36:47 -07:00
unlikelyzero
3de70079dd remove unused gha 2022-03-30 11:36:15 -07:00
63 changed files with 388 additions and 2468 deletions

View File

@@ -149,15 +149,11 @@ workflows:
node-version: lts/fermium
browser: ChromeHeadless
post-steps:
- upload_code_covio
- upload_code_covio
- unit-test:
name: node16-chrome
node-version: lts/gallium
browser: ChromeHeadless
- unit-test:
name: node18-chrome
node-version: "18"
browser: ChromeHeadless
browser: ChromeHeadless
- e2e-test:
name: e2e-ci
node-version: lts/gallium
@@ -180,10 +176,6 @@ workflows:
name: node16-chrome-nightly
node-version: lts/gallium
browser: ChromeHeadless
- unit-test:
name: node18-chrome
node-version: "18"
browser: ChromeHeadless
- npm-audit:
node-version: lts/gallium
- e2e-test:

View File

@@ -8,7 +8,7 @@ on:
jobs:
e2e-full:
if: ${{ github.event.label.name == 'pr:e2e' }}
if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'pr:e2e' }} || ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -30,11 +30,21 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright@1.19.2 install
check-latest: true
- name: Cache node modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- run: npm install
- run: npx playwright install
- run: npm run test:e2e:full
- name: Archive test results
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
path: test-results
- name: Test success

View File

@@ -10,15 +10,25 @@ on:
jobs:
e2e-visual:
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }} || ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright@1.19.2 install
check-latest: true
- name: Cache node modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- run: npm install
- run: npx playwright install
- name: Run the e2e visual tests
run: npm run test:e2e:visual
env:

View File

@@ -1,21 +0,0 @@
name: "e2e"
on:
workflow_dispatch:
inputs:
version:
description: 'Which branch do you want to test?' # Limited to branch for now
required: false
default: 'master'
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npm install
- name: Run the e2e tests
run: npm run test:e2e:ci

View File

@@ -6,9 +6,6 @@ on:
description: 'Which branch do you want to test?' # Limited to branch for now
required: false
default: 'master'
pull_request:
types:
- labeled
jobs:
lighthouse-pr:
if: ${{ github.event.label.name == 'pr:lighthouse' }}
@@ -56,6 +53,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: '16'
check-latest: true
- name: Cache node modules
uses: actions/cache@v2
env:
@@ -82,6 +80,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: '16'
check-latest: true
- name: Cache node modules
uses: actions/cache@v3
env:

View File

@@ -27,6 +27,7 @@ jobs:
with:
node-version: 16
registry-url: https://registry.npmjs.org/
check-latest: true
- run: npm install
- run: npm publish --access public --tag unstable
env:

View File

@@ -12,13 +12,14 @@ jobs:
fail-fast: false
matrix:
os:
- ubuntu-latest
- ubuntu-20.04 #MMOC Ubuntu version
- macos-latest
- macos-10.15
- windows-latest
node_version:
- 14
- 16
- 18
- 17
architecture:
- x64
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
@@ -29,6 +30,16 @@ jobs:
with:
node-version: ${{ matrix.node_version }}
architecture: ${{ matrix.architecture }}
check-latest: true
- name: Cache node modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-${{ matrix.node_version }}--build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-${{ matrix.node_version }}--build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- run: npm install
- run: npm test
- run: npm run lint -- --quiet

View File

@@ -1,190 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
Test for plot autoscale.
*/
const { test: _test, expect } = require('@playwright/test');
// create a new `test` API that will not append platform details to snapshot
// file names, only for the tests in this file, so that the same snapshots will
// be used for all platforms.
const test = _test.extend({
_autoSnapshotSuffix: [
async ({}, use, testInfo) => {
testInfo.snapshotSuffix = '';
await use();
},
{ auto: true }
]
});
test.use({
viewport: {
width: 1280,
height: 720
}
});
test.describe('ExportAsJSON', () => {
test.only('autoscale off causes no error from undefined user range', async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' });
await setTimeRange(page);
await createSinewaveOverlayPlot(page);
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
await turnOffAutoscale(page);
const canvas = page.locator('canvas').nth(1);
// Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior.
await Promise.all([
testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']),
new Promise(r => setTimeout(r, 100))
.then(() => canvas.screenshot())
.then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-prepan.png', { maxDiffPixels: 40 }))
]);
let errorCount = 0;
function onError() {
errorCount++;
}
page.on('pageerror', onError);
await page.keyboard.down('Alt');
await canvas.dragTo(canvas, {
sourcePosition: {
x: 200,
y: 200
},
targetPosition: {
x: 400,
y: 400
}
});
await page.keyboard.up('Alt');
page.off('pageerror', onError);
// There would have been an error at this point. So if there isn't, then
// we fixed it.
expect(errorCount).toBe(0);
// Ensure the drag worked.
await Promise.all([
testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']),
new Promise(r => setTimeout(r, 100))
.then(() => canvas.screenshot())
.then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-panned.png', { maxDiffPixels: 20 }))
]);
});
});
/**
* @param {import('@playwright/test').Page} page
* @param {string} start
* @param {string} end
*/
async function setTimeRange(page, start = '2022-03-29 22:00:00.000Z', end = '2022-03-29 22:00:30.000Z') {
// Set a specific time range for consistency, otherwise it will change
// on every test to a range based on the current time.
const timeInputs = page.locator('input.c-input--datetime');
await timeInputs.first().click();
await timeInputs.first().fill(start);
await timeInputs.nth(1).click();
await timeInputs.nth(1).fill(end);
}
/**
* @param {import('@playwright/test').Page} page
*/
async function createSinewaveOverlayPlot(page) {
// click create button
await page.locator('button:has-text("Create")').click();
// add overlay plot with defaults
await page.locator('li:has-text("Overlay Plot")').click();
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/a9268c6f-45cc-4bcd-a6a0-50ac4036e396?tc.mode=fixed&tc.startBound=1649305424163&tc.endBound=1649307224163&tc.timeSystem=utc&view=plot-overlay' }*/),
page.locator('text=OK').click()
]);
// save (exit edit mode)
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page.locator('text=Save and Finish Editing').click();
// click create button
await page.locator('button:has-text("Create")').click();
// add sine wave generator with defaults
await page.locator('li:has-text("Sine Wave Generator")').click();
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/a9268c6f-45cc-4bcd-a6a0-50ac4036e396/5cfa5c69-17bc-4a99-9545-4da8125380c5?tc.mode=fixed&tc.startBound=1649305424163&tc.endBound=1649307224163&tc.timeSystem=utc&view=plot-single' }*/),
page.locator('text=OK').click()
]);
// focus the overlay plot
await page.locator('text=Open MCT My Items >> span').nth(3).click();
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/a9268c6f-45cc-4bcd-a6a0-50ac4036e396?tc.mode=fixed&tc.startBound=1649305424163&tc.endBound=1649307224163&tc.timeSystem=utc&view=plot-overlay' }*/),
page.locator('text=Unnamed Overlay Plot').first().click()
]);
}
/**
* @param {import('@playwright/test').Page} page
*/
async function turnOffAutoscale(page) {
// enter edit mode
await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
// uncheck autoscale
await page.locator('text=Y Axis Scaling Auto scale Padding >> input[type="checkbox"]').uncheck();
// save
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page.locator('text=Save and Finish Editing').click();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function testYTicks(page, values) {
const yTicks = page.locator('.gl-plot-y-tick-label');
let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
for (let i = 0, l = values.length; i < l; i += 1) {
promises.push(expect(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line
}
await Promise.all(promises);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -195,7 +195,6 @@
));
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
openmct.install(openmct.plugins.Timer());
openmct.install(openmct.plugins.Timelist());
openmct.start();
</script>
</html>

View File

@@ -1,12 +1,12 @@
{
"name": "openmct",
"version": "2.0.3-SNAPSHOT",
"version": "2.0.2-SNAPSHOT",
"description": "The Open MCT core platform",
"devDependencies": {
"@babel/eslint-parser": "7.16.3",
"@braintree/sanitize-url": "6.0.0",
"@percy/cli": "1.0.4",
"@percy/playwright": "1.0.2",
"@percy/cli": "1.0.0-beta.76",
"@percy/playwright": "1.0.1",
"@playwright/test": "1.19.2",
"@types/eventemitter3": "^1.0.0",
"@types/jasmine": "^4.0.1",
@@ -18,12 +18,13 @@
"babel-plugin-istanbul": "6.1.1",
"comma-separated-values": "3.6.4",
"copy-webpack-plugin": "10.2.0",
"core-js": "3.21.1",
"cross-env": "7.0.3",
"css-loader": "4.0.0",
"d3-axis": "1.0.x",
"d3-scale": "1.0.x",
"d3-selection": "1.3.x",
"eslint": "8.13.0",
"eslint": "8.11.0",
"eslint-plugin-compat": "4.0.2",
"eslint-plugin-playwright": "0.8.0",
"eslint-plugin-vue": "8.5.0",
@@ -37,7 +38,7 @@
"imports-loader": "0.8.0",
"jasmine-core": "4.0.1",
"jsdoc": "3.5.5",
"karma": "6.3.18",
"karma": "6.3.15",
"karma-chrome-launcher": "3.1.1",
"karma-cli": "2.0.0",
"karma-coverage": "2.1.1",
@@ -46,14 +47,14 @@
"karma-jasmine": "4.0.1",
"karma-junit-reporter": "2.0.1",
"karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.34",
"karma-spec-reporter": "0.0.33",
"karma-webpack": "5.0.0",
"lighthouse": "9.5.0",
"location-bar": "3.0.1",
"lodash": "4.17.21",
"mini-css-extract-plugin": "2.6.0",
"moment": "2.29.1",
"moment-duration-format": "2.3.2",
"moment-duration-format": "2.2.2",
"moment-timezone": "0.5.34",
"node-bourbon": "4.2.3",
"painterro": "1.2.56",
@@ -61,14 +62,14 @@
"plotly.js-gl2d-dist": "2.5.0",
"printj": "1.3.1",
"request": "2.88.2",
"resolve-url-loader": "5.0.0",
"resolve-url-loader": "4.0.0",
"sass": "1.49.9",
"sass-loader": "12.6.0",
"sinon": "13.0.1",
"style-loader": "^1.0.1",
"uuid": "3.3.3",
"vue": "2.6.14",
"vue-eslint-parser": "8.3.0",
"vue-eslint-parser": "8.2.0",
"vue-loader": "15.9.8",
"vue-template-compiler": "2.6.14",
"webpack": "5.68.0",
@@ -111,9 +112,6 @@
"engines": {
"node": ">=14.19.1"
},
"overrides": {
"core-js": "3.21.1"
},
"browserslist": [
"Firefox ESR",
"not IE 11",

View File

@@ -1,5 +1,5 @@
<template>
<div class="c-table c-table--sortable c-list-view c-list-view--sticky-header c-list-view--selectable">
<div class="c-table c-table--sortable c-list-view">
<table class="c-table__body">
<thead class="c-table__header">
<tr>

View File

@@ -0,0 +1,32 @@
/******************************* LIST VIEW */
.c-list-view {
overflow-x: auto !important;
overflow-y: auto;
tbody tr {
background: $colorListItemBg;
transition: $transOut;
}
body.desktop & {
tbody tr {
cursor: pointer;
&:hover {
background: $colorListItemBgHov;
filter: $filterHov;
transition: $transIn;
}
}
}
td {
$p: floor($interiorMargin * 1.5);
@include ellipsize();
line-height: 120%; // Needed for icon alignment
max-width: 0;
padding-top: $p;
padding-bottom: $p;
width: 25%;
}
}

View File

@@ -2,16 +2,11 @@ import ImageryViewComponent from './components/ImageryView.vue';
import Vue from 'vue';
const DEFAULT_IMAGE_FRESHNESS_OPTIONS = {
fadeOutDelayTime: '0s',
fadeOutDurationTime: '30s'
};
export default class ImageryView {
constructor(openmct, domainObject, objectPath, options) {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.options = options;
this.component = undefined;
}
@@ -32,7 +27,6 @@ export default class ImageryView {
openmct: this.openmct,
domainObject: this.domainObject,
objectPath: alternateObjectPath || this.objectPath,
imageFreshnessOptions: this.options?.imageFreshness || DEFAULT_IMAGE_FRESHNESS_OPTIONS,
currentView: this
},
data() {

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
import ImageryView from './ImageryView';
export default function ImageryViewProvider(openmct, options) {
export default function ImageryViewProvider(openmct) {
const type = 'example.imagery';
function hasImageTelemetry(domainObject) {
@@ -43,7 +43,7 @@ export default function ImageryViewProvider(openmct, options) {
return hasImageTelemetry(domainObject) && (!isChildOfTimeStrip || openmct.router.isNavigatedObject(objectPath));
},
view: function (domainObject, objectPath) {
return new ImageryView(openmct, domainObject, objectPath, options);
return new ImageryView(openmct, domainObject, objectPath);
}
};
}

View File

@@ -132,10 +132,6 @@
<!-- image fresh -->
<div
v-if="canTrackDuration"
:style="{
'animation-delay': imageFreshnessOptions.fadeOutDelayTime,
'animation-duration': imageFreshnessOptions.fadeOutDurationTime
}"
:class="{'c-imagery--new': isImageNew && !refreshCSS}"
class="c-imagery__age icon-timer"
>{{ formattedDuration }}</div>
@@ -239,7 +235,7 @@ export default {
ImageControls
},
mixins: [imageryData],
inject: ['openmct', 'domainObject', 'objectPath', 'currentView', 'imageFreshnessOptions'],
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
props: {
focusedImageTimestamp: {
type: Number,
@@ -536,7 +532,7 @@ export default {
this.updateRelatedTelemetryForFocusedImage = _.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
// for resizing the object view
this.resizeImageContainer = _.debounce(this.resizeImageContainer, 400, { leading: true });
this.resizeImageContainer = _.debounce(this.resizeImageContainer, 400);
if (this.$refs.imageBG) {
this.imageContainerResizeObserver = new ResizeObserver(this.resizeImageContainer);

View File

@@ -1,13 +1,3 @@
@keyframes fade-out {
from {
background-color: rgba($colorOk, 0.5);
}
to {
background-color: rgba($colorOk, 0);
color: inherit;
}
}
.c-imagery {
display: flex;
flex-direction: column;
@@ -133,17 +123,12 @@
// New imagery
$bgColor: $colorOk;
color: $colorOkFg;
background-color: rgba($bgColor, 0.5);
animation-name: fade-out;
animation-timing-function: ease-in;
animation-iteration-count: 1;
animation-fill-mode: forwards;
background: rgba($bgColor, 0.5);
@include flash($animName: flashImageAge, $iter: 2, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0));
}
&__thumbs-wrapper {
display: flex; // Uses row layout
justify-content: flex-end;
&.is-autoscroll-off {
background: $colorInteriorBorder;

View File

@@ -23,9 +23,9 @@
import ImageryViewProvider from './ImageryViewProvider';
import ImageryTimestripViewProvider from './ImageryTimestripViewProvider';
export default function (options) {
export default function () {
return function install(openmct) {
openmct.objectViews.addProvider(new ImageryViewProvider(openmct, options));
openmct.objectViews.addProvider(new ImageryViewProvider(openmct));
openmct.objectViews.addProvider(new ImageryTimestripViewProvider(openmct));
};
}

View File

@@ -35,8 +35,6 @@
ref="searchResults"
:domain-object="domainObject"
:results="searchResults"
@cancelEdit="cancelTransaction"
@editingEntry="startTransaction"
@changeSectionPage="changeSelectedSection"
@updateEntries="updateEntries"
/>
@@ -142,8 +140,6 @@
:selected-page="selectedPage"
:selected-section="selectedSection"
:read-only="false"
@cancelEdit="cancelTransaction"
@editingEntry="startTransaction"
@deleteEntry="deleteEntry"
@updateEntry="updateEntry"
/>
@@ -714,8 +710,6 @@ export default {
notebookEntries[this.selectedSection.id][this.selectedPage.id] = entries;
mutateObject(this.openmct, this.domainObject, 'configuration.entries', notebookEntries);
this.saveTransaction();
},
getPageIdFromUrl() {
return this.openmct.router.getParams().pageId;
@@ -752,39 +746,6 @@ export default {
this.selectPage(pageId);
this.syncUrlWithPageAndSection();
},
activeTransaction() {
return this.openmct.objects.getActiveTransaction();
},
startTransaction() {
if (!this.openmct.editor.isEditing()) {
this.openmct.objects.startTransaction();
}
},
saveTransaction() {
const transaction = this.activeTransaction();
if (!transaction || this.openmct.editor.isEditing()) {
return;
}
return transaction.commit()
.catch(error => {
throw error;
}).finally(() => {
this.openmct.objects.endTransaction();
});
},
cancelTransaction() {
if (!this.openmct.editor.isEditing()) {
const transaction = this.activeTransaction();
transaction.cancel()
.catch(error => {
throw error;
}).finally(() => {
this.openmct.objects.endTransaction();
});
}
}
}
};

View File

@@ -55,7 +55,6 @@
class="c-ne__text c-ne__input"
tabindex="0"
contenteditable
@focus="editingEntry()"
@blur="updateEntryValue($event)"
@keydown.enter.exact.prevent
@keyup.enter.exact.prevent="forceBlur($event)"
@@ -285,16 +284,11 @@ export default {
this.$emit('updateEntry', this.entry);
},
editingEntry() {
this.$emit('editingEntry');
},
updateEntryValue($event) {
const value = $event.target.innerText;
if (value !== this.entry.text && value.match(/\S/)) {
this.entry.text = value;
this.$emit('updateEntry', this.entry);
} else {
this.$emit('cancelEdit');
}
}
}

View File

@@ -33,8 +33,6 @@
:read-only="true"
:selected-page="result.page"
:selected-section="result.section"
@editingEntry="editingEntry"
@cancelEdit="cancelEdit"
@changeSectionPage="changeSectionPage"
@updateEntries="updateEntries"
/>
@@ -65,12 +63,6 @@ export default {
}
},
methods: {
editingEntry() {
this.$emit('editingEntry');
},
cancelEdit() {
this.$emit('cancelEdit');
},
changeSectionPage(data) {
this.$emit('changeSectionPage', data);
},

View File

@@ -37,7 +37,6 @@
v-if="seriesModels.length > 0"
:tick-width="tickWidth"
:single-series="seriesModels.length === 1"
:has-same-range-value="hasSameRangeValue"
:series-model="seriesModels[0]"
:style="{
left: (plotWidth - tickWidth) + 'px'
@@ -251,8 +250,7 @@ export default {
loaded: false,
isTimeOutOfSync: false,
showLimitLineLabels: undefined,
isFrozenOnMouseDown: false,
hasSameRangeValue: true
isFrozenOnMouseDown: false
};
},
computed: {
@@ -364,7 +362,6 @@ export default {
this.setDisplayRange(series, xKey);
}, this);
this.listenTo(series, 'change:yKey', () => {
this.checkSameRangeValue();
this.loadSeriesData(series);
}, this);
@@ -372,18 +369,10 @@ export default {
this.loadSeriesData(series);
}, this);
this.checkSameRangeValue();
this.loadSeriesData(series);
},
checkSameRangeValue() {
this.hasSameRangeValue = this.seriesModels.every((model) => {
return model.get('yKey') === this.seriesModels[0].get('yKey');
});
},
removeSeries(plotSeries) {
this.checkSameRangeValue();
this.stopListening(plotSeries);
},
@@ -499,7 +488,7 @@ export default {
},
setDisplayRange(series, xKey) {
if (this.config.series.models.length !== 1) {
if (this.config.series.length !== 1) {
return;
}

View File

@@ -29,9 +29,9 @@
>
<div
v-if="canShowYAxisLabel"
v-if="singleSeries"
class="gl-plot-label gl-plot-y-label"
:class="{'icon-gear': (yKeyOptions.length > 1 && singleSeries)}"
:class="{'icon-gear': (yKeyOptions.length > 1)}"
>{{ yAxisLabel }}
</div>
@@ -76,12 +76,6 @@ export default {
return true;
}
},
hasSameRangeValue: {
type: Boolean,
default() {
return true;
}
},
seriesModel: {
type: Object,
default() {
@@ -101,11 +95,6 @@ export default {
loaded: false
};
},
computed: {
canShowYAxisLabel() {
return this.singleSeries === true || this.hasSameRangeValue === true;
}
},
mounted() {
this.yAxis = this.getYAxisFromConfig();
this.loaded = true;

View File

@@ -157,8 +157,7 @@ export default class PlotConfigurationModel extends Model {
@typedef {{
configuration: {
series: import('./PlotSeries').PlotSeriesModelType[]
yAxis: import('./YAxisModel').YAxisModelType
},
}
}} SomeDomainObject_NeedsName
*/

View File

@@ -250,7 +250,6 @@ export default class PlotSeries extends Model {
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
}.bind(this);
this.set('unit', valueMetadata.unit);
const format = this.formats[newKey];
this.getYVal = format.parse.bind(format);
}

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Model from './Model';
import Model from "./Model";
/**
* @extends {Model<XAxisModelType, XAxisModelOptions>}
@@ -49,11 +49,11 @@ export default class XAxisModel extends Model {
}
});
this.on('change:frozen', (frozen) => {
this.on('change:frozen', ((frozen) => {
if (!frozen) {
this.set('range', this.get('range'));
}
});
}));
if (this.get('range')) {
this.set('range', this.get('range'));
@@ -126,7 +126,7 @@ export default class XAxisModel extends Model {
/**
@typedef {import("./Model").ModelType<{
range?: NumberRange
range: NumberRange
displayRange: NumberRange
frozen: boolean
label: string

View File

@@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import _ from 'lodash';
import Model from './Model';
/**
@@ -62,14 +63,14 @@ export default class YAxisModel extends Model {
*/
listenToSeriesCollection(seriesCollection) {
this.seriesCollection = seriesCollection;
this.listenTo(this.seriesCollection, 'add', series => {
this.listenTo(this.seriesCollection, 'add', (series => {
this.trackSeries(series);
this.updateFromSeries(this.seriesCollection);
}, this);
this.listenTo(this.seriesCollection, 'remove', series => {
}), this);
this.listenTo(this.seriesCollection, 'remove', (series => {
this.untrackSeries(series);
this.updateFromSeries(this.seriesCollection);
}, this);
}), this);
this.seriesCollection.forEach(this.trackSeries, this);
this.updateFromSeries(this.seriesCollection);
}
@@ -139,11 +140,11 @@ export default class YAxisModel extends Model {
}
resetStats() {
this.unset('stats');
this.seriesCollection.forEach(series => {
this.seriesCollection.forEach(function (series) {
if (series.has('stats')) {
this.updateStats(series.get('stats'));
}
});
}, this);
}
/**
* @param {import('./PlotSeries').default} series
@@ -169,18 +170,7 @@ export default class YAxisModel extends Model {
if (autoscale && this.has('stats')) {
this.set('displayRange', this.applyPadding(this.get('stats')));
} else {
const range = this.get('range');
if (range) {
// If we already have a user-defined range, make sure it maps to the
// range we'll actually use for the ticks.
this.set('displayRange', range);
} else {
// Otherwise use the last known displayRange as the initial
// values for the user-defined range, so that we don't end up
// with any error from an undefined user range.
this.set('range', this.get('displayRange'));
}
this.set('displayRange', this.get('range'));
}
}
/**
@@ -189,7 +179,7 @@ export default class YAxisModel extends Model {
*/
updateFromSeries(seriesCollection) {
const plotModel = this.plot.get('domainObject');
const label = plotModel?.configuration?.yAxis?.label;
const label = _.get(plotModel, 'configuration.yAxis.label');
const sampleSeries = seriesCollection.first();
if (!sampleSeries) {
if (!label) {
@@ -205,19 +195,19 @@ export default class YAxisModel extends Model {
this.set('format', yFormat.format.bind(yFormat));
this.set('values', yMetadata.values);
if (!label) {
const labelName = seriesCollection
.map(s => (s.metadata ? s.metadata.value(s.get('yKey')).name : ''))
.reduce((a, b) => {
if (a === undefined) {
return b;
}
const labelName = seriesCollection.map(function (s) {
return s.metadata ? s.metadata.value(s.get('yKey')).name : '';
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
if (a === b) {
return a;
}
return '';
}, undefined);
return '';
}, undefined);
if (labelName) {
this.set('label', labelName);
@@ -225,19 +215,19 @@ export default class YAxisModel extends Model {
return;
}
const labelUnits = seriesCollection
.map(s => (s.metadata ? s.metadata.value(s.get('yKey')).units : ''))
.reduce((a, b) => {
if (a === undefined) {
return b;
}
const labelUnits = seriesCollection.map(function (s) {
return s.metadata ? s.metadata.value(s.get('yKey')).units : '';
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
if (a === b) {
return a;
}
return '';
}, undefined);
return '';
}, undefined);
if (labelUnits) {
this.set('label', labelUnits);
@@ -249,17 +239,14 @@ export default class YAxisModel extends Model {
/**
* @override
* @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
* @returns {Partial<YAxisModelType>}
* @returns {YAxisModelType}
*/
defaultModel(options) {
// @ts-ignore incomplete YAxisModelType object used for default value.
return {
frozen: false,
autoscale: true,
autoscalePadding: 0.1
// 'range' is not specified here, it is undefined at first. When the
// user turns off autoscale, the current 'displayRange' is used for
// the initial value of 'range'.
};
}
}
@@ -270,7 +257,7 @@ export default class YAxisModel extends Model {
@typedef {import('./XAxisModel').AxisModelType & {
autoscale: boolean
autoscalePadding: number
stats?: import('./XAxisModel').NumberRange
stats: import('./XAxisModel').NumberRange
values: Array<TODO>
}} YAxisModelType
*/

View File

@@ -52,10 +52,10 @@
class="l-inspector-part"
>
<div
v-show="!autoscale && validationErrors.range"
v-show="!autoscale && validation.range"
class="grid-span-all form-error"
>
{{ validationErrors.range }}
{{ validation.range }}
</div>
<li class="grid-row force-border">
<div
@@ -88,7 +88,7 @@
</template>
<script>
import { objectPath } from "./formUtil";
import { objectPath, validate, coerce } from "./formUtil";
import _ from "lodash";
export default {
@@ -108,7 +108,7 @@ export default {
autoscalePadding: '',
rangeMin: '',
rangeMax: '',
validationErrors: {}
validation: {}
};
},
mounted() {
@@ -178,6 +178,12 @@ export default {
if (Number(range.min) > Number(range.max)) {
return 'Minimum must be less than Maximum.';
}
if (model.get('autoscale')) {
return false;
}
return true;
}
}
];
@@ -186,9 +192,14 @@ export default {
this.label = this.yAxis.get('label');
this.autoscale = this.yAxis.get('autoscale');
this.autoscalePadding = this.yAxis.get('autoscalePadding');
const range = this.yAxis.get('range') ?? this.yAxis.get('displayRange');
this.rangeMin = range?.min;
this.rangeMax = range?.max;
const range = this.yAxis.get('range');
if (!range) {
this.rangeMin = undefined;
this.rangeMax = undefined;
} else {
this.rangeMin = range.min;
this.rangeMax = range.max;
}
},
updateForm(formKey) {
let newVal;
@@ -201,27 +212,26 @@ export default {
newVal = this[formKey];
}
let oldVal = this.yAxis.get(formKey);
const oldVal = this.yAxis.get(formKey);
const formField = this.fields.find((field) => field.modelProp === formKey);
const validationError = formField.validate?.(newVal, this.yAxis);
this.validationErrors[formKey] = validationError;
if (validationError) {
const path = objectPath(formField.objectPath);
const validationResult = validate(newVal, this.yAxis, formField.validate);
if (validationResult === true) {
delete this.validation[formKey];
} else {
this.validation[formKey] = validationResult;
return;
}
newVal = formField.coerce?.(newVal) ?? newVal;
oldVal = formField.coerce?.(oldVal) ?? oldVal;
const path = objectPath(formField.objectPath);
if (!_.isEqual(newVal, oldVal)) {
// TODO: Why do we mutate yAxis twice, once directly, once via objects.mutate? Or are they different objects?
this.yAxis.set(formKey, newVal);
if (!_.isEqual(coerce(newVal, formField.coerce), coerce(oldVal, formField.coerce))) {
this.yAxis.set(formKey, coerce(newVal, formField.coerce));
if (path) {
this.openmct.objects.mutate(
this.domainObject,
path(this.domainObject, this.yAxis),
newVal
coerce(newVal, formField.coerce)
);
}
}

View File

@@ -15,5 +15,15 @@ export function validate(value, model, validateFunc) {
}
export function objectPath(path) {
return path && typeof path !== 'function' ? () => path : path;
if (path) {
if (typeof path !== "function") {
const staticObjectPath = path;
return function (object, model) {
return staticObjectPath;
};
}
return path;
}
}

View File

@@ -571,34 +571,6 @@ describe("the plugin", function () {
range: 2
}
}]
},
configuration: {
objectStyles: {
staticStyle: {
style: {
backgroundColor: 'rgb(0, 200, 0)',
color: '',
border: ''
}
},
conditionSetIdentifier: {
namespace: '',
key: 'testConditionSetId'
},
selectedConditionId: 'conditionId1',
defaultConditionId: 'conditionId1',
styles: [
{
conditionId: 'conditionId1',
style: {
backgroundColor: 'rgb(0, 155, 0)',
color: '',
output: '',
border: ''
}
}
]
}
}
};
@@ -843,20 +815,6 @@ describe("the plugin", function () {
});
});
it("shows styles for telemetry objects if available", (done) => {
Vue.nextTick(() => {
let conditionalStylesContainer = element.querySelectorAll(".c-plot--stacked-container .js-style-receiver");
let hasStyles = 0;
conditionalStylesContainer.forEach(el => {
if (el.style.backgroundColor !== '') {
hasStyles++;
}
});
expect(hasStyles).toBe(1);
done();
});
});
describe('limits', () => {
it('lines are not displayed by default', () => {

View File

@@ -26,10 +26,8 @@
import MctPlot from '../MctPlot.vue';
import Vue from "vue";
import conditionalStylesMixin from "./mixins/objectStyles-mixin";
export default {
mixins: [conditionalStylesMixin],
inject: ['openmct', 'domainObject', 'path'],
props: {
object: {

View File

@@ -1,143 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
export default {
inject: ['openmct', 'domainObject', 'path'],
data() {
return {
objectStyle: undefined
};
},
mounted() {
this.objectStyles = this.getObjectStyleForItem(this.object.configuration);
this.initObjectStyles();
},
beforeDestroy() {
if (this.stopListeningStyles) {
this.stopListeningStyles();
}
if (this.styleRuleManager) {
this.styleRuleManager.destroy();
}
},
methods: {
getObjectStyleForItem(config) {
if (config && config.objectStyles) {
return config.objectStyles ? Object.assign({}, config.objectStyles) : undefined;
} else {
return undefined;
}
},
initObjectStyles() {
if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager(this.objectStyles, this.openmct, this.updateStyle.bind(this), true);
} else {
this.styleRuleManager.updateObjectStyleConfig(this.objectStyles);
}
if (this.stopListeningStyles) {
this.stopListeningStyles();
}
this.stopListeningStyles = this.openmct.objects.observe(this.object, 'configuration.objectStyles', (newObjectStyle) => {
//Updating styles in the inspector view will trigger this so that the changes are reflected immediately
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
});
if (this.object && this.object.configuration && this.object.configuration.fontStyle) {
const { fontSize, font } = this.object.configuration.fontStyle;
this.setFontSize(fontSize);
this.setFont(font);
}
this.stopListeningFontStyles = this.openmct.objects.observe(this.object, 'configuration.fontStyle', (newFontStyle) => {
this.setFontSize(newFontStyle.fontSize);
this.setFont(newFontStyle.font);
});
},
getStyleReceiver() {
let styleReceiver;
if (this.$el !== undefined) {
styleReceiver = this.$el.querySelector('.js-style-receiver')
|| this.$el.querySelector(':first-child');
if (styleReceiver === null) {
styleReceiver = undefined;
}
}
return styleReceiver;
},
setFontSize(newSize) {
let elemToStyle = this.getStyleReceiver();
if (elemToStyle !== undefined) {
elemToStyle.dataset.fontSize = newSize;
}
},
setFont(newFont) {
let elemToStyle = this.getStyleReceiver();
if (elemToStyle !== undefined) {
elemToStyle.dataset.font = newFont;
}
},
updateStyle(styleObj) {
let elemToStyle = this.getStyleReceiver();
if (!styleObj || elemToStyle === undefined) {
return;
}
let keys = Object.keys(styleObj);
keys.forEach(key => {
if (elemToStyle) {
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
if (elemToStyle.style[key]) {
elemToStyle.style[key] = '';
}
} else {
if (!styleObj.isStyleInvisible && elemToStyle.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
elemToStyle.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
} else if (styleObj.isStyleInvisible && !elemToStyle.classList.contains(styleObj.isStyleInvisible)) {
elemToStyle.classList.add(styleObj.isStyleInvisible);
}
elemToStyle.style[key] = styleObj[key];
}
}
});
if (this.object && this.object.type === 'conditionWidget' && keys.includes('output')) {
this.openmct.objects.mutate(this.object, 'conditionalLabel', styleObj.output);
} else {
this.openmct.objects.mutate(this.object, 'conditionalLabel', '');
}
}
}
};

View File

@@ -76,8 +76,7 @@ define([
'./timer/plugin',
'./userIndicator/plugin',
'../../example/exampleUser/plugin',
'./localStorage/plugin',
'./timelist/plugin'
'./localStorage/plugin'
], function (
_,
UTCTimeSystem,
@@ -134,8 +133,7 @@ define([
Timer,
UserIndicator,
ExampleUser,
LocalStorage,
TimeList
LocalStorage
) {
const plugins = {};
@@ -212,7 +210,6 @@ define([
plugins.DeviceClassifier = DeviceClassifier.default;
plugins.UserIndicator = UserIndicator.default;
plugins.LocalStorage = LocalStorage.default;
plugins.Timelist = TimeList.default;
return plugins;
});

View File

@@ -9,11 +9,7 @@ export default class TelemetryTableView {
this.objectPath = objectPath;
this.component = undefined;
Object.defineProperty(this, 'table', {
value: new TelemetryTable(domainObject, openmct),
enumerable: false,
configurable: false
});
this.table = new TelemetryTable(domainObject, openmct);
}
getViewContext() {

View File

@@ -1,394 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<template>
<div
ref="timelistHolder"
class="c-timelist"
>
<list-view
:items="planActivities"
:header-items="headerItems"
:default-sort="defaultSort"
class="sticky"
/>
</div>
</template>
<script>
import {getValidatedPlan} 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 moment from "moment";
import uuid from "uuid";
const SCROLL_TIMEOUT = 10000;
const ROW_HEIGHT = 30;
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss:SSS';
const headerItems = [
{
defaultDirection: true,
isSortable: true,
property: 'start',
name: 'Start Time',
format: function (value, object) {
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`;
}
}, {
defaultDirection: false,
property: 'duration',
name: 'Time To/From',
format: function (value) {
let result;
if (value < 0) {
result = `-${getPreciseDuration(Math.abs(value))}`;
} else if (value > 0) {
result = `+${getPreciseDuration(value)}`;
} else {
result = 'Now';
}
return result;
}
}, {
defaultDirection: true,
property: 'name',
name: 'Activity'
}
];
const defaultSort = {
property: 'start',
defaultDirection: true
};
export default {
components: {
ListView
},
inject: ['openmct', 'domainObject', 'path'],
data() {
return {
viewBounds: undefined,
height: 0,
planActivities: [],
headerItems: headerItems,
defaultSort: defaultSort
};
},
mounted() {
this.isEditing = this.openmct.editor.isEditing();
this.timestamp = Date.now();
this.getPlanDataAndSetConfig(this.domainObject);
this.unlisten = this.openmct.objects.observe(this.domainObject, 'selectFile', this.getPlanDataAndSetConfig);
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.editor.on('isEditing', this.setEditState);
this.deferAutoScroll = _.debounce(this.deferAutoScroll, 500);
this.$el.parentElement.addEventListener('scroll', this.deferAutoScroll, true);
},
beforeDestroy() {
if (this.unlisten) {
this.unlisten();
}
if (this.unlistenConfig) {
this.unlistenConfig();
}
if (this.unlistenTicker) {
this.unlistenTicker();
}
if (this.removeStatusListener) {
this.removeStatusListener();
}
this.openmct.editor.off('isEditing', this.setEditState);
this.$el.parentElement.removeEventListener('scroll', this.deferAutoScroll, true);
if (this.clearAutoScrollDisabledTimer) {
clearTimeout(this.clearAutoScrollDisabledTimer);
}
},
methods: {
getPlanDataAndSetConfig(mutatedObject) {
this.getPlanData(mutatedObject);
this.setViewFromConfig(mutatedObject.configuration);
},
setViewFromConfig(configuration) {
if (this.isEditing) {
this.filterValue = configuration.filter;
this.hideAll = false;
this.showAll = true;
this.listActivities();
} else {
this.filterValue = configuration.filter;
this.setSort();
this.setViewBounds();
this.listActivities();
}
},
getPlanData(domainObject) {
this.planData = getValidatedPlan(domainObject);
},
setViewBounds() {
const pastEventsIndex = this.domainObject.configuration.pastEventsIndex;
const currentEventsIndex = this.domainObject.configuration.currentEventsIndex;
const futureEventsIndex = this.domainObject.configuration.futureEventsIndex;
const pastEventsDuration = this.domainObject.configuration.pastEventsDuration;
const pastEventsDurationIndex = this.domainObject.configuration.pastEventsDurationIndex;
const futureEventsDuration = this.domainObject.configuration.futureEventsDuration;
const futureEventsDurationIndex = this.domainObject.configuration.futureEventsDurationIndex;
if (pastEventsIndex === 0 && futureEventsIndex === 0 && currentEventsIndex === 0) {
//show all events
this.showAll = false;
this.viewBounds = undefined;
this.hideAll = true;
return;
}
this.hideAll = false;
if (pastEventsIndex === 1 && futureEventsIndex === 1 && currentEventsIndex === 1) {
//show all events
this.showAll = true;
this.viewBounds = undefined;
return;
}
this.showAll = false;
this.viewBounds = {};
this.noCurrent = currentEventsIndex === 0;
if (pastEventsIndex !== 1) {
const pastDurationInMS = this.getDurationInMilliSeconds(pastEventsDuration, pastEventsDurationIndex);
this.viewBounds.pastEnd = (timestamp) => {
if (pastEventsIndex === 2) {
return timestamp - pastDurationInMS;
} else if (pastEventsIndex === 0) {
return timestamp + 1;
}
};
}
if (futureEventsIndex !== 1) {
const futureDurationInMS = this.getDurationInMilliSeconds(futureEventsDuration, futureEventsDurationIndex);
this.viewBounds.futureStart = (timestamp) => {
if (futureEventsIndex === 2) {
return timestamp + futureDurationInMS;
} else if (futureEventsIndex === 0) {
return 0;
}
};
}
},
getDurationInMilliSeconds(duration, durationIndex) {
if (duration > 0) {
if (durationIndex === 0) {
return duration * 1000;
} else if (durationIndex === 1) {
return duration * 60 * 1000;
} else if (durationIndex === 2) {
return duration * 60 * 60 * 1000;
}
}
},
listActivities() {
let groups = Object.keys(this.planData);
let activities = [];
groups.forEach((key) => {
activities = activities.concat(this.planData[key]);
});
activities = activities.filter(this.filterActivities);
activities = this.applyStyles(activities);
this.setScrollTop();
// sort by start time
this.planActivities = activities.sort(this.sortByStartTime);
},
clearPreviousActivities(time) {
if (time instanceof Date) {
this.timestamp = time.getTime();
} else {
this.timestamp = time;
}
this.listActivities();
},
filterActivities(activity, index) {
const hasFilterMatch = this.filterByName(activity.name);
if (hasFilterMatch === false || this.hideAll === true) {
return false;
}
if (this.showAll === true) {
return true;
}
//current event or future start event or past end event
const isCurrent = (this.noCurrent === false && this.timestamp >= activity.start && this.timestamp <= activity.end);
const isPast = (this.timestamp > activity.end && (this.viewBounds.pastEnd === undefined || activity.end >= this.viewBounds.pastEnd(this.timestamp)));
const isFuture = (this.timestamp < activity.start && (this.viewBounds.futureStart === undefined || activity.start <= this.viewBounds.futureStart(this.timestamp)));
return isCurrent || isPast || isFuture;
},
filterByName(name) {
const filters = this.filterValue.split(',');
return filters.some((search => {
const normalized = search.trim().toLowerCase();
const regex = new RegExp(normalized);
return regex.test(name.toLowerCase());
}));
},
applyStyles(activities) {
let firstCurrentActivityIndex = -1;
let currentActivitiesCount = 0;
const styledActivities = activities.map((activity, index) => {
if (this.timestamp >= activity.start && this.timestamp <= activity.end) {
activity.cssClass = '--is-current';
if (firstCurrentActivityIndex < 0) {
firstCurrentActivityIndex = index;
}
currentActivitiesCount = currentActivitiesCount + 1;
} else if (this.timestamp < activity.start) {
activity.cssClass = '--is-future';
} else {
activity.cssClass = '--is-past';
}
if (!activity.key) {
activity.key = uuid();
}
activity.duration = activity.start - this.timestamp;
return activity;
});
this.firstCurrentActivityIndex = firstCurrentActivityIndex;
this.currentActivitiesCount = currentActivitiesCount;
return styledActivities;
},
canAutoScroll() {
//this distinguishes between programmatic vs user-triggered scroll events
this.autoScrolled = (this.dontAutoScroll !== true);
return this.autoScrolled;
},
resetScroll() {
if (this.canAutoScroll() === false) {
return;
}
this.firstCurrentActivityIndex = -1;
this.currentActivitiesCount = 0;
this.$el.parentElement.scrollTo({top: 0});
this.autoScrolled = false;
},
setScrollTop() {
//scroll to somewhere mid-way of the current activities
if (this.firstCurrentActivityIndex > -1) {
if (this.canAutoScroll() === false) {
return;
}
const scrollOffset = this.currentActivitiesCount > 0 ? Math.floor(this.currentActivitiesCount / 2) : 0;
this.$el.parentElement.scrollTo({
top: ROW_HEIGHT * (this.firstCurrentActivityIndex + scrollOffset),
behavior: "smooth"
});
this.autoScrolled = false;
} else {
this.resetScroll();
}
},
deferAutoScroll() {
//if this is not a user-triggered event, don't defer auto scrolling
if (this.autoScrolled) {
this.autoScrolled = false;
return;
}
this.dontAutoScroll = true;
const self = this;
if (this.clearAutoScrollDisabledTimer) {
clearTimeout(this.clearAutoScrollDisabledTimer);
}
this.clearAutoScrollDisabledTimer = setTimeout(() => {
self.dontAutoScroll = false;
self.setScrollTop();
}, SCROLL_TIMEOUT);
},
setSort() {
const sortOrder = SORT_ORDER_OPTIONS[this.domainObject.configuration.sortOrderIndex];
const property = sortOrder.property;
const direction = sortOrder.direction.toLowerCase() === 'asc';
this.defaultSort = {
property,
defaultDirection: direction
};
},
sortByStartTime(a, b) {
const numA = parseInt(a.start, 10);
const numB = parseInt(b.start, 10);
return numA - numB;
},
setStatus(status) {
this.status = status;
},
setEditState(isEditing) {
this.isEditing = isEditing;
this.setViewFromConfig(this.domainObject.configuration);
}
}
};
</script>

View File

@@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Timelist from './Timelist.vue';
import { TIMELIST_TYPE } from './constants';
import Vue from 'vue';
export default function TimelistViewProvider(openmct) {
return {
key: 'timelist.view',
name: 'Time List',
cssClass: 'icon-timelist',
canView(domainObject) {
return domainObject.type === TIMELIST_TYPE;
},
canEdit(domainObject) {
return domainObject.type === TIMELIST_TYPE;
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
Timelist
},
provide: {
openmct,
domainObject,
path: objectPath
},
template: '<timelist></timelist>'
});
},
destroy: function () {
component.$destroy();
component = undefined;
}
};
}
};
}

View File

@@ -1,24 +0,0 @@
export const SORT_ORDER_OPTIONS = [
{
label: 'Start ascending',
property: 'start',
direction: 'ASC'
},
{
label: 'Start descending',
property: 'start',
direction: 'DESC'
},
{
label: 'End ascending',
property: 'end',
direction: 'ASC'
},
{
label: 'End descending',
property: 'end',
direction: 'DESC'
}
];
export const TIMELIST_TYPE = 'timelist';

View File

@@ -1,124 +0,0 @@
<template>
<li class="c-inspect-properties__row">
<div
class="c-inspect-properties__label"
title="Options for future events."
>{{ label }}</div>
<div
v-if="canEdit"
class="c-inspect-properties__value"
>
<select
v-model="index"
@change="updateForm('index')"
>
<option
v-for="(activityOption, activityKey) in activitiesOptions"
:key="activityKey"
:value="activityKey"
>{{ activityOption }}</option>
</select>
<input
v-if="index === 2"
v-model="duration"
class="c-input c-input--sm"
type="text"
@change="updateForm('duration')"
>
<select
v-if="index === 2"
v-model="durationIndex"
@change="updateForm('durationIndex')"
>
<option
v-for="(durationOption, durationKey) in durationOptions"
:key="durationKey"
:value="durationKey"
>{{ durationOption }}</option>
</select>
</div>
<div
v-else
class="c-inspect-properties__value"
>
{{ activitiesOptions[index] }} <span v-if="index > 1">{{ duration }} {{ durationOptions[durationIndex] }}</span>
</div>
</li>
</template>
<script>
const ACTIVITIES_OPTIONS = [
'Don\'t show',
'Show all',
'Show starts within',
'Show after end for'
];
const DURATION_OPTIONS = [
'seconds',
'minutes',
'hours'
];
export default {
inject: ['openmct', 'domainObject'],
props: {
label: {
type: String,
required: true
},
prefix: {
type: String,
required: true
}
},
data() {
return {
index: this.domainObject.configuration[`${this.prefix}Index`],
durationIndex: this.domainObject.configuration[`${this.prefix}DurationIndex`],
duration: this.domainObject.configuration[`${this.prefix}Duration`],
activitiesOptions: ACTIVITIES_OPTIONS,
durationOptions: DURATION_OPTIONS,
isEditing: this.openmct.editor.isEditing()
};
},
computed: {
canEdit() {
return this.isEditing && !this.domainObject.locked;
}
},
mounted() {
if (this.prefix === 'futureEvents') {
this.activitiesOptions = ACTIVITIES_OPTIONS.slice(0, 3);
} else if (this.prefix === 'pastEvents') {
this.activitiesOptions = ACTIVITIES_OPTIONS.filter((item, index) => index !== 2);
} else if (this.prefix === 'currentEvents') {
this.activitiesOptions = ACTIVITIES_OPTIONS.slice(0, 2);
}
this.openmct.editor.on('isEditing', this.setEditState);
},
beforeDestroy() {
this.openmct.editor.off('isEditing', this.setEditState);
},
methods: {
updateForm(property) {
if (!this.isValid()) {
return;
}
const capitalized = property.charAt(0).toUpperCase() + property.substr(1);
this.$emit('updated', {
property: `${this.prefix}${capitalized}`,
value: this[property]
});
},
isValid() {
return this.index < 2 || (this.durationIndex >= 0 && this.duration > 0);
},
setEditState(isEditing) {
this.isEditing = isEditing;
}
}
};
</script>

View File

@@ -1,91 +0,0 @@
<template>
<li class="c-inspect-properties__row">
<div
v-if="canEdit"
class="c-inspect-properties__hint span-all"
>Filter this view by comma-separated keywords.</div>
<div
class="c-inspect-properties__label"
title="Filter by keyword."
>Filters</div>
<div
v-if="canEdit"
class="c-inspect-properties__value"
:class="{'form-error': hasError}"
>
<textarea
v-model="filterValue"
class="c-input--flex"
type="text"
@keydown.enter.exact.stop="forceBlur($event)"
@keyup="updateForm($event, 'filter')"
></textarea>
</div>
<div
v-else
class="c-inspect-properties__value"
>
{{ filterValue }}
</div>
</li>
</template>
<script>
export default {
inject: ['openmct', 'domainObject'],
data() {
return {
isEditing: this.openmct.editor.isEditing(),
filterValue: this.domainObject.configuration.filter,
hasError: false
};
},
computed: {
canEdit() {
return this.isEditing && !this.domainObject.locked;
}
},
mounted() {
this.openmct.editor.on('isEditing', this.setEditState);
},
beforeDestroy() {
this.openmct.editor.off('isEditing', this.setEditState);
},
methods: {
setEditState(isEditing) {
this.isEditing = isEditing;
if (!this.isEditing && this.hasError) {
this.filterValue = this.domainObject.configuration.filter;
this.hasError = false;
}
},
forceBlur(event) {
event.target.blur();
},
updateForm(event, property) {
if (!this.isValid()) {
this.hasError = true;
return;
}
this.hasError = false;
this.$emit('updated', {
property,
value: this.filterValue.replace(/,(\s)*$/, '')
});
},
isValid() {
// Test for any word character, any whitespace character or comma
if (this.filterValue === '') {
return true;
}
const regex = new RegExp(/^([a-zA-Z0-9_\-\s,])+$/g);
return regex.test(this.filterValue);
}
}
};
</script>

View File

@@ -1,70 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import TimelistPropertiesView from "./TimelistPropertiesView.vue";
import { TIMELIST_TYPE } from '../constants';
import Vue from 'vue';
export default function TimeListInspectorViewProvider(openmct) {
return {
key: 'timelist-inspector',
name: 'Timelist Inspector View',
canView: function (selection) {
if (selection.length === 0 || selection[0].length === 0) {
return false;
}
let context = selection[0][0].context;
return context && context.item
&& context.item.type === TIMELIST_TYPE;
},
view: function (selection) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
TimelistPropertiesView: TimelistPropertiesView
},
provide: {
openmct,
domainObject: selection[0][0].context.item
},
template: '<timelist-properties-view></timelist-properties-view>'
});
},
destroy: function () {
if (component) {
component.$destroy();
component = undefined;
}
}
};
},
priority: function () {
return 1;
}
};
}

View File

@@ -1,146 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<template>
<div class="c-timelist-properties">
<div class="c-inspect-properties">
<ul class="c-inspect-properties__section">
<div
class="c-inspect-properties_header"
title="'Timeframe options'"
>Timeframe</div>
<li class="c-inspect-properties__row">
<div
v-if="canEdit"
class="c-inspect-properties__hint span-all"
>These settings are not previewed and will be applied after editing is completed.</div>
<div
class="c-inspect-properties__label"
title="Sort order of the timelist."
>Sort Order</div>
<div
v-if="canEdit"
class="c-inspect-properties__value"
>
<select
v-model="sortOrderIndex"
@change="updateSortOrder()"
>
<option
v-for="(sortOrderOption, index) in sortOrderOptions"
:key="index"
:value="index"
>{{ sortOrderOption.label }}</option>
</select>
</div>
<div
v-else
class="c-inspect-properties__value"
>
{{ sortOrderOptions[sortOrderIndex].label }}
</div>
</li>
<event-properties
v-for="type in eventTypes"
:key="type.prefix"
:label="type.label"
:prefix="type.prefix"
@updated="eventPropertiesUpdated"
/>
</ul>
</div>
<div class="c-inspect-properties">
<ul class="c-inspect-properties__section">
<div
class="c-inspect-properties_header"
title="'Filters'"
>Filtering</div>
<filtering @updated="eventPropertiesUpdated" />
</ul>
</div>
</div>
</template>
<script>
import EventProperties from './EventProperties.vue';
import { SORT_ORDER_OPTIONS } from '../constants';
import Filtering from './Filtering.vue';
const EVENT_TYPES = [
{
label: 'Future Events',
prefix: 'futureEvents'
},
{
label: 'Current Events',
prefix: 'currentEvents'
},
{
label: 'Past Events',
prefix: 'pastEvents'
}
];
export default {
components: {
Filtering,
EventProperties
},
inject: ['openmct', 'domainObject'],
data() {
return {
sortOrderIndex: this.domainObject.configuration.sortOrderIndex,
sortOrderOptions: SORT_ORDER_OPTIONS,
eventTypes: EVENT_TYPES,
isEditing: this.openmct.editor.isEditing()
};
},
computed: {
canEdit() {
return this.isEditing && !this.domainObject.locked;
}
},
mounted() {
this.openmct.editor.on('isEditing', this.setEditState);
},
beforeDestroy() {
this.openmct.editor.off('isEditing', this.setEditState);
},
methods: {
setEditState(isEditing) {
this.isEditing = isEditing;
},
updateSortOrder() {
this.updateProperty('sortOrderIndex', this.sortOrderIndex);
},
updateProperty(key, value) {
this.openmct.objects.mutate(this.domainObject, `configuration.${key}`, value);
},
eventPropertiesUpdated(data) {
const key = data.property;
const value = data.value;
this.updateProperty(key, value);
}
}
};
</script>

View File

@@ -1,68 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import TimelistViewProvider from './TimelistViewProvider';
import { TIMELIST_TYPE } from './constants';
import TimeListInspectorViewProvider from "./inspector/TimeListInspectorViewProvider";
export default function () {
return function install(openmct) {
openmct.types.addType(TIMELIST_TYPE, {
name: 'Time List',
key: TIMELIST_TYPE,
description: 'A configurable, time-ordered list view of activities for a compatible mission plan file.',
creatable: true,
cssClass: 'icon-timelist',
form: [
{
name: 'Upload Plan (JSON File)',
key: 'selectFile',
control: 'file-input',
required: true,
text: 'Select File...',
type: 'application/json',
property: [
"selectFile"
]
}
],
initialize: function (domainObject) {
domainObject.configuration = {
sortOrderIndex: 0,
futureEventsIndex: 0,
futureEventsDurationIndex: 0,
futureEventsDuration: 20,
currentEventsIndex: 1,
currentEventsDurationIndex: 0,
currentEventsDuration: 20,
pastEventsIndex: 0,
pastEventsDurationIndex: 0,
pastEventsDuration: 20,
filter: ''
};
}
});
openmct.objectViews.addProvider(new TimelistViewProvider(openmct));
openmct.inspectorViews.addProvider(new TimeListInspectorViewProvider(openmct));
};
}

View File

@@ -1,181 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {createOpenMct, resetApplicationState} from "utils/testing";
import TimelistPlugin from "./plugin";
import { TIMELIST_TYPE } from "./constants";
import Vue from 'vue';
import moment from "moment";
const LIST_ITEM_CLASS = '.js-table__body .js-list-item';
const LIST_ITEM_VALUE_CLASS = '.js-list-item__value';
const LIST_ITEM_BODY_CLASS = '.js-table__body th';
describe('the plugin', function () {
let timelistDefinition;
let element;
let child;
let openmct;
let appHolder;
let originalRouterPath;
beforeEach((done) => {
appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
openmct.install(new TimelistPlugin());
timelistDefinition = openmct.types.get(TIMELIST_TYPE).definition;
element = document.createElement('div');
element.style.width = '640px';
element.style.height = '480px';
child = document.createElement('div');
child.style.width = '640px';
child.style.height = '480px';
element.appendChild(child);
originalRouterPath = openmct.router.path;
openmct.on('start', done);
openmct.start(appHolder);
});
afterEach(() => {
openmct.router.path = originalRouterPath;
return resetApplicationState(openmct);
});
let mockTimelistObject = {
name: 'Timelist',
key: TIMELIST_TYPE,
creatable: true
};
it('defines a timelist object type with the correct key', () => {
expect(timelistDefinition.key).toEqual(mockTimelistObject.key);
});
it('is creatable', () => {
expect(timelistDefinition.creatable).toEqual(mockTimelistObject.creatable);
});
describe('the timelist view', () => {
it('provides a timelist view', () => {
const testViewObject = {
id: "test-object",
type: TIMELIST_TYPE
};
openmct.router.path = [testViewObject];
const applicableViews = openmct.objectViews.get(testViewObject, [testViewObject]);
let timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
expect(timelistView).toBeDefined();
});
});
describe('the timelist view displays activities', () => {
let timelistDomainObject;
let timelistView;
beforeEach(() => {
timelistDomainObject = {
identifier: {
key: 'test-object',
namespace: ''
},
type: TIMELIST_TYPE,
id: "test-object",
configuration: {
sortOrderIndex: 0,
futureEventsIndex: 1,
futureEventsDurationIndex: 0,
futureEventsDuration: 20,
currentEventsIndex: 1,
currentEventsDurationIndex: 0,
currentEventsDuration: 20,
pastEventsIndex: 1,
pastEventsDurationIndex: 0,
pastEventsDuration: 20,
filter: ''
},
selectFile: {
body: JSON.stringify({
"TEST-GROUP": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
"start": 1597170002854,
"end": 1597171032854,
"type": "TEST-GROUP",
"color": "fuchsia",
"textColor": "black"
},
{
"name": "Sed ut perspiciatis",
"start": 1597171132854,
"end": 1597171232854,
"type": "TEST-GROUP",
"color": "fuchsia",
"textColor": "black"
}
]
})
}
};
openmct.router.path = [timelistDomainObject];
const applicableViews = openmct.objectViews.get(timelistDomainObject, [timelistDomainObject]);
timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
let view = timelistView.view(timelistDomainObject, []);
view.show(child, true);
return Vue.nextTick();
});
it('displays the activities', () => {
const items = element.querySelectorAll(LIST_ITEM_CLASS);
expect(items.length).toEqual(2);
});
it('displays the activity headers', () => {
const headers = element.querySelectorAll(LIST_ITEM_BODY_CLASS);
expect(headers.length).toEqual(4);
});
it('displays activity details', (done) => {
Vue.nextTick(() => {
const itemEls = element.querySelectorAll(LIST_ITEM_CLASS);
const itemValues = itemEls[0].querySelectorAll(LIST_ITEM_VALUE_CLASS);
expect(itemValues.length).toEqual(4);
expect(itemValues[3].innerHTML.trim()).toEqual('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua');
expect(itemValues[0].innerHTML.trim()).toEqual(`${moment(1597170002854).format('YYYY-MM-DD HH:mm:ss:SSS')}Z`);
expect(itemValues[1].innerHTML.trim()).toEqual(`${moment(1597171032854).format('YYYY-MM-DD HH:mm:ss:SSS')}Z`);
done();
});
});
});
});

View File

@@ -1,55 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.c-timelist {
& .nowMarker.hasCurrent {
height: 2px;
position: absolute;
z-index: 10;
background: cyan;
width: 100%;
}
.c-list-item {
/* Time Lists */
&.--is-current {
background-color: $colorCurrentBg;
border-top: 1px solid $colorCurrentBorder !important;
color: $colorCurrentFg;
font-weight: bold;
}
&.--is-future {
background-color: $colorFutureBg;
border-top-color: $colorFutureBorder !important;
color: $colorFutureFg;
}
&__value {
&.--duration {
width: 5%;
}
}
}
}

View File

@@ -38,41 +38,41 @@ export default class ViewLargeAction {
}
invoke(objectPath, view) {
performance.mark('viewlarge.start');
const childElement = view?.parentElement?.firstChild;
const parentElement = view.parentElement;
let childElement = parentElement && parentElement.firstChild;
if (!childElement) {
const message = "ViewLargeAction: missing element";
this.openmct.notifications.error(message);
throw new Error(message);
}
this._expand(objectPath, view);
this._expand(objectPath, childElement);
}
appliesTo(objectPath, view) {
const childElement = view?.parentElement?.firstChild;
return childElement && !childElement.classList.contains('js-main-container')
appliesTo(objectPath, view = {}) {
const parentElement = view.parentElement;
const element = parentElement && parentElement.firstChild;
const viewLargeAction = element && !element.classList.contains('js-main-container')
&& !this.openmct.router.isNavigatedObject(objectPath);
return viewLargeAction;
}
_expand(objectPath, view) {
const element = this._getPreview(objectPath, view);
_expand(objectPath, childElement) {
const parentElement = childElement.parentElement;
this.overlay = this.openmct.overlays.overlay({
element,
element: this._getPreview(objectPath),
size: 'large',
autoHide: false,
onDestroy: () => {
this.preview.$destroy();
this.preview = undefined;
delete this.preview;
onDestroy() {
parentElement.append(childElement);
}
});
}
_getPreview(objectPath, view) {
this.preview = new Vue({
_getPreview(objectPath) {
const preview = new Vue({
components: {
Preview
},
@@ -80,14 +80,9 @@ export default class ViewLargeAction {
openmct: this.openmct,
objectPath
},
data() {
return {
view
};
},
template: '<Preview :existing-view="view"></Preview>'
template: '<Preview></Preview>'
});
return this.preview.$mount().$el;
return preview.$mount().$el;
}
}

View File

@@ -345,7 +345,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: #575757;
$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -366,14 +366,6 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: $colorTabHeaderBg;
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBorder: $colorBodyBg;
$colorFutureBg: rgba($colorKey, 0.2);
$colorFutureFg: $colorCurrentFg;
$colorFutureBorder: $colorCurrentBorder;
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(#fff, 0.1);

View File

@@ -349,7 +349,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: #575757;
$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -370,14 +370,6 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: rgba($colorBodyFg, 0.15);
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBorder: #fff;
$colorFutureBg: rgba($colorKey, 0.2);
$colorFutureFg: $colorCurrentFg;
$colorFutureBorder: $colorCurrentBorder;
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(#fff, 0.03);

View File

@@ -345,7 +345,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: #e2e2e2;
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -366,14 +366,6 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: rgba($colorBodyFg, 0.15);
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.4);
// Time Strip and Lists
$colorCurrentBg: rgba($colorStatusAlert, 0.3);
$colorCurrentFg: pullForward($colorBodyFg, 20%);
$colorCurrentBorder: #fff;
$colorFutureBg: rgba($colorKey, 0.2);
$colorFutureFg: $colorCurrentFg;
$colorFutureBorder: $colorCurrentBorder;
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(black, 0.07);

View File

@@ -263,7 +263,6 @@ $glyph-icon-telemetry-aggregate: '\eb2b';
$glyph-icon-bar-chart: '\eb2c';
$glyph-icon-map: '\eb2d';
$glyph-icon-plan: '\eb2e';
$glyph-icon-timelist: '\eb2f';
/************************** GLYPHS AS DATA URI */
// Only objects have been converted, for use in Create menu and folder views
@@ -318,4 +317,3 @@ $bg-icon-condition-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='h
$bg-icon-bar-chart: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM133.82 448H64V224h69.82Zm104.73 0h-69.82V64h69.82Zm104.72 0h-69.82V288h69.82ZM448 448h-69.82V128H448Z'/%3e%3c/svg%3e");
$bg-icon-map: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 32.7 384 64v448l64-31.3c35.2-17.21 64-60.1 64-95.3v-320c0-35.2-28.8-49.91-64-32.7ZM160 456l193.6 48.4v-448L160 8v448zM129.6.4 128 0 64 31.3C28.8 48.51 0 91.4 0 126.6v320c0 35.2 28.8 49.91 64 32.7l64-31.3 1.6.4Z'/%3e%3c/svg%3e");
$bg-icon-plan: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cg data-name='Layer 1'%3e%3cpath fill='%23000000' d='M128 96V64a64.19 64.19 0 0 1 64-64h128a64.19 64.19 0 0 1 64 64v32Z'/%3e%3cpath fill='%23000000' d='M416 64v64H96V64c-52.8 0-96 43.2-96 96v256c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V160c0-52.8-43.2-96-96-96ZM64 288v-64h128v64Zm256 128H128v-64h192Zm128 0h-64v-64h64Zm0-128H256v-64h192Z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e");
$bg-icon-timelist: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M448 0H64A64.19 64.19 0 0 0 0 64v384a64.19 64.19 0 0 0 64 64h384a64.19 64.19 0 0 0 64-64V64a64.19 64.19 0 0 0-64-64ZM213.47 266.73a24 24 0 0 1-32.2 10.74L104 238.83V128a24 24 0 0 1 48 0v81.17l50.73 25.36a24 24 0 0 1 10.74 32.2ZM448 448H288v-64h160Zm0-96H288v-64h160Zm0-96H288v-64h160Zm0-96H288V96h160Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e");

View File

@@ -194,7 +194,6 @@
.icon-bar-chart { @include glyphBefore($glyph-icon-bar-chart); }
.icon-map { @include glyphBefore($glyph-icon-map); }
.icon-plan { @include glyphBefore($glyph-icon-plan); }
.icon-timelist { @include glyphBefore($glyph-icon-timelist); }
/************************** 12 PX CLASSES */
// TODO: sync with 16px redo as of 10/25/18
@@ -257,4 +256,3 @@
.bg-icon-bar-chart { @include glyphBg($bg-icon-bar-chart); }
.bg-icon-map { @include glyphBg($bg-icon-map); }
.bg-icon-plan { @include glyphBg($bg-icon-plan); }
.bg-icon-timelist { @include glyphBg($bg-icon-timelist); }

File diff suppressed because it is too large Load Diff

View File

@@ -162,5 +162,4 @@
<glyph unicode="&#xeb2c;" glyph-name="icon-bar-graph" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM267.64-64h-139.64v448h139.64zM477.1-64h-139.64v768h139.64zM686.54-64h-139.64v320h139.64zM896-64h-139.64v640h139.64z" />
<glyph unicode="&#xeb2d;" glyph-name="icon-map" d="M896 766.6l-128-62.6v-896l128 62.6c70.4 34.42 128 120.2 128 190.6v640c0 70.4-57.6 99.82-128 65.4zM320-80l387.2-96.8v896l-387.2 96.8v-896zM259.2 831.2l-3.2 0.8-128-62.6c-70.4-34.42-128-120.2-128-190.6v-640c0-70.4 57.6-99.82 128-65.4l128 62.6 3.2-0.8z" />
<glyph unicode="&#xeb2e;" glyph-name="icon-plan" d="M256 640v64c0.215 70.606 57.394 127.785 127.979 128h256.021c70.606-0.215 127.785-57.394 128-127.979v-64.021zM832 704v-128h-640v128c-105.6 0-192-86.4-192-192v-512c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v512c0 105.6-86.4 192-192 192zM128 256v128h256v-128zM640 0h-384v128h384zM896 0h-128v128h128zM896 256h-384v128h384z" />
<glyph unicode="&#xeb2f;" glyph-name="icon-timelist" d="M896 832h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM426.94 298.54c-8.054-15.864-24.249-26.545-42.938-26.545-7.823 0-15.209 1.871-21.734 5.191l0.273-0.126-154.54 77.28v221.66c0 26.51 21.49 48 48 48s48-21.49 48-48v0-162.34l101.46-50.72c15.864-8.054 26.545-24.249 26.545-42.938 0-7.823-1.871-15.209-5.191-21.734l0.126 0.273zM896-64h-320v128h320zM896 128h-320v128h320zM896 320h-320v128h320zM896 512h-320v128h320z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -15,6 +15,7 @@
@import "../plugins/flexibleLayout/components/flexible-layout.scss";
@import "../plugins/folderView/components/grid-view.scss";
@import "../plugins/folderView/components/list-item.scss";
@import "../plugins/folderView/components/list-view.scss";
@import "../plugins/imagery/components/imagery-view.scss";
@import "../plugins/imagery/components/Compass/compass.scss";
@import "../plugins/telemetryTable/components/table-row.scss";
@@ -27,7 +28,6 @@
@import "../plugins/timeConductor/conductor-mode-icon.scss";
@import "../plugins/timeConductor/date-picker.scss";
@import "../plugins/timeline/timeline.scss";
@import "../plugins/timelist/timelist.scss";
@import "../plugins/plan/plan";
@import "../plugins/viewDatumAction/components/metadata-list.scss";
@import "../ui/components/object-frame.scss";
@@ -37,7 +37,6 @@
@import "../ui/components/swim-lane/swimlane.scss";
@import "../ui/components/toggle-switch.scss";
@import "../ui/components/timesystem-axis.scss";
@import "../ui/components/List/list-view.scss";
@import "../ui/inspector/elements.scss";
@import "../ui/inspector/inspector.scss";
@import "../ui/inspector/location.scss";

View File

@@ -1,56 +0,0 @@
<template>
<th
v-if="isSortable"
class="is-sortable"
:class="{
'is-sorting': currentSort === property,
'asc': direction,
'desc': !direction
}"
@click="sort(property, direction)"
>
{{ title }}
</th>
<th v-else>
{{ title }}
</th>
</template>
<script>
export default {
inject: ['openmct'],
props: {
property: {
type: String,
required: true
},
currentSort: {
type: String,
required: true
},
title: {
type: String,
required: true
},
direction: {
type: Boolean,
required: true
},
isSortable: {
type: Boolean,
default() {
return false;
}
}
},
methods: {
sort(property, direction) {
this.$emit('sort', {
property,
direction
});
}
}
};
</script>

View File

@@ -1,53 +0,0 @@
<template>
<tr
class="c-list-item js-list-item"
:class="item.cssClass || ''"
>
<td
v-for="itemValue in formattedItemValues"
:key="itemValue.key"
class="c-list-item__value js-list-item__value"
:class="['--' + itemValue.key]"
:title="itemValue.text"
>
{{ itemValue.text }}
</td>
</tr>
</template>
<script>
import _ from 'lodash';
export default {
inject: ['openmct'],
props: {
item: {
type: Object,
required: true
},
itemProperties: {
type: Array,
required: true
}
},
computed: {
formattedItemValues() {
let values = [];
this.itemProperties.forEach((property) => {
// 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);
}
values.push({
text: value,
key: property.key
});
});
return values;
}
}
};
</script>

View File

@@ -1,142 +0,0 @@
<template>
<div class="c-table c-table--sortable c-list-view c-list-view--sticky-header">
<table class="c-table__body js-table__body">
<thead class="c-table__header">
<tr>
<list-header
v-for="headerItem in headerItems"
:key="headerItem.property"
:direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection"
:is-sortable="headerItem.isSortable"
:title="headerItem.name"
:property="headerItem.property"
:current-sort="sortBy"
@sort="sort"
/>
</tr>
</thead>
<tbody>
<list-item
v-for="item in sortedItems"
:key="item.key"
:item="item"
:item-properties="itemProperties"
/>
</tbody>
</table>
</div>
</template>
<script>
import ListItem from './ListItem.vue';
import ListHeader from './ListHeader.vue';
import _ from 'lodash';
export default {
components: {
ListItem,
ListHeader
},
inject: ['domainObject', 'openmct'],
props: {
headerItems: {
type: Array,
required: true
},
items: {
type: Array,
required: true
},
defaultSort: {
type: Object,
default() {
return {
property: '',
defaultDirection: true
};
}
},
storageKey: {
type: String,
default() {
return undefined;
}
}
},
data() {
let sortBy = this.defaultSort.property;
let ascending = this.defaultSort.defaultDirection;
if (this.storageKey) {
let persistedSortOrder = window.localStorage.getItem(this.storageKey);
if (persistedSortOrder) {
let parsed = JSON.parse(persistedSortOrder);
sortBy = parsed.sortBy;
ascending = parsed.ascending;
}
}
return {
sortBy,
ascending
};
},
computed: {
sortedItems() {
let sortedItems = _.sortBy(this.items, this.sortBy);
if (!this.ascending) {
sortedItems = sortedItems.reverse();
}
return sortedItems;
},
itemProperties() {
return this.headerItems.map((headerItem) => {
return {
key: headerItem.property,
format: headerItem.format
};
});
}
},
watch: {
defaultSort: {
handler() {
this.setSort();
},
deep: true
}
},
methods: {
setSort() {
this.sortBy = this.defaultSort.property;
this.ascending = this.defaultSort.defaultDirection;
},
sort(data) {
const property = data.property;
const direction = data.direction;
if (this.sortBy === property) {
this.ascending = !this.ascending;
} else {
this.sortBy = property;
this.ascending = direction;
}
if (this.storageKey) {
window.localStorage
.setItem(
this.storageKey,
JSON.stringify(
{
sortBy: this.sortBy,
ascending: this.ascending
}
)
);
}
}
}
};
</script>

View File

@@ -1,40 +0,0 @@
/******************************* LIST VIEW */
.c-list-view {
tbody tr {
background: $colorListItemBg;
transition: $transOut;
}
td {
$p: floor($interiorMargin * 1.5);
@include ellipsize();
line-height: 120%; // Needed for icon alignment
max-width: 0;
padding-top: $p;
padding-bottom: $p;
width: 25%;
}
&--selectable {
body.desktop & {
tbody tr {
cursor: pointer;
&:hover {
background: $colorListItemBgHov;
filter: $filterHov;
transition: $transIn;
}
}
}
}
&--sticky-header {
thead tr {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 2;
}
}
}

View File

@@ -79,11 +79,6 @@
padding-right: 0;
}
textarea {
// When a textarea is in the Inspector, only allow vertical resize
resize: vertical;
}
/************************************************************** LEGACY */
.l-inspector-part {
display: contents;
@@ -156,8 +151,7 @@
padding: 3px $interiorMarginLg 3px 0;
}
&__label,
&__hint {
&__label {
color: $colorInspectorPropName;
&[title]:not([title=""]) {
@@ -173,14 +167,6 @@
grid-column: 1 / 3;
}
}
.is-editing & {
.c-inspect-properties {
&__value, &__label {
line-height: 160%; // Prevent buttons/selects from overlapping when wrapping
}
}
}
}
/********************************************* INSPECTOR PROPERTIES TAB */

View File

@@ -22,10 +22,10 @@
<template>
<div class="l-preview-window js-preview-window">
<PreviewHeader
:current-view="currentViewProvider"
:current-view="currentView"
:action-collection="actionCollection"
:domain-object="domainObject"
:views="viewProviders"
:views="views"
/>
<div class="l-preview-window__object-view js-notebook-snapshot-item">
<div ref="objectView"></div>
@@ -52,12 +52,6 @@ export default {
default() {
return undefined;
}
},
existingView: {
type: Object,
default() {
return undefined;
}
}
},
data() {
@@ -66,25 +60,21 @@ export default {
return {
domainObject: domainObject,
viewKey: undefined,
viewProviders: [],
currentViewProvider: {},
actionCollection: undefined,
existingViewIndex: 0
views: [],
currentView: {},
actionCollection: undefined
};
},
mounted() {
this.viewProviders = this.openmct.objectViews.get(this.domainObject, this.objectPath);
this.viewProviders.forEach((provider, index) => {
provider.onItemClicked = () => {
if (this.existingView && provider.key === this.existingView.key) {
this.existingViewIndex = index;
}
this.setView(provider);
this.views = this.openmct.objectViews.get(this.domainObject, this.objectPath).map((view) => {
view.onItemClicked = () => {
return this.setView(view);
};
return view;
});
this.setView(this.viewProviders[0]);
this.setView(this.views[0]);
},
beforeDestroy() {
if (this.stopListeningStyles) {
@@ -101,74 +91,41 @@ export default {
}
},
destroyed() {
if (!this.existingView) {
this.view.destroy();
} else if (this.existingViewElement) {
// if the existing view element is populated, it's the currently viewed view
// in preview and we need to add it back to the parent.
this.addExistingViewBackToParent();
}
this.view.destroy();
},
methods: {
clear() {
if (this.view) {
if (this.view !== this.existingView) {
this.view.destroy();
} else {
this.addExistingViewBackToParent();
}
this.view.destroy();
this.$refs.objectView.innerHTML = '';
delete this.view;
delete this.viewContainer;
}
delete this.view;
delete this.viewContainer;
},
setView(viewProvider) {
if (this.viewKey === viewProvider.key) {
setView(view) {
if (this.viewKey === view.key) {
return;
}
const isExistingView = viewProvider.key === this.existingView?.key;
this.clear();
this.viewKey = viewProvider.key;
this.initializeViewContainer();
if (isExistingView) {
this.view = this.existingView;
this.existingViewElement = this.existingView.parentElement.firstChild;
this.currentViewProvider = this.viewProviders[this.existingViewIndex];
} else {
this.currentViewProvider = viewProvider;
this.view = this.currentViewProvider.view(this.domainObject, this.objectPath);
}
this.getActionsCollection(this.view);
if (isExistingView) {
this.viewContainer.appendChild(this.existingViewElement);
} else {
this.view.show(this.viewContainer, false, this.viewOptions);
}
this.initObjectStyles();
},
addExistingViewBackToParent() {
this.existingView.parentElement.appendChild(this.existingViewElement);
delete this.existingViewElement;
},
initializeViewContainer() {
this.viewKey = view.key;
this.currentView = view;
this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('l-angular-ov-wrapper');
this.$refs.objectView.append(this.viewContainer);
this.view = this.currentView.view(this.domainObject, this.objectPath);
this.getActionsCollection();
this.view.show(this.viewContainer, false, this.viewOptions);
this.initObjectStyles();
},
getActionsCollection(view) {
getActionsCollection() {
if (this.actionCollection) {
this.actionCollection.destroy();
}
this.actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, view);
this.actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.view);
},
initObjectStyles() {
if (!this.styleRuleManager) {

View File

@@ -90,7 +90,6 @@ define(['EventEmitter'], function (EventEmitter) {
provider.view = (domainObject, objectPath) => {
const viewObject = wrappedView(domainObject, objectPath);
const wrappedShow = viewObject.show.bind(viewObject);
viewObject.key = key; // provide access to provider key on view object
viewObject.show = (element, isEditing, viewOptions) => {
viewObject.parentElement = element.parentElement;
wrappedShow(element, isEditing, viewOptions);