Compare commits
6 Commits
vista-r4.9
...
coverage-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18137fe125 | ||
|
|
51a6ff7825 | ||
|
|
f989733e81 | ||
|
|
5a6162eb4c | ||
|
|
b0a2f8dd8b | ||
|
|
54bed23267 |
@@ -22,7 +22,7 @@ commands:
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install:
|
||||
install-npm: true
|
||||
node-version: << parameters.node-version >>
|
||||
node-version: lts/fermium
|
||||
- run: npm install
|
||||
restore_cache_cmd:
|
||||
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -24,7 +24,7 @@ assignees: ''
|
||||
- [ ] Regression? Did this used to work or has it always been broken?
|
||||
- [ ] Is there a workaround available?
|
||||
- [ ] Does this impact a critical component?
|
||||
- [ ] Is this just a visual bug with no functional impact?
|
||||
- [ ] Is this just a visual bug?
|
||||
|
||||
#### Steps to Reproduce
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
|
||||
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@@ -16,7 +16,6 @@ updates:
|
||||
- dependency-name: "*jasmine*"
|
||||
- dependency-name: "*playwright*"
|
||||
- dependency-name: "*percy*"
|
||||
- dependency-name: "*vue-loader*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
|
||||
1
e2e/localstorage.json
Normal file
1
e2e/localstorage.json
Normal file
@@ -0,0 +1 @@
|
||||
{"tcHistory":"{\"utc\":[{\"start\":1640401741152,\"end\":1640403541152}]}","mct":"{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1640403542253,\"modified\":1640403542253},\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\":{\"identifier\":{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"},\"name\":\"All DomainObjects\",\"type\":\"folder\",\"composition\":[{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"}],\"modified\":1640403603547,\"location\":\"mine\",\"persisted\":1640403603547},\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\":{\"identifier\":{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},\"name\":\"Unnamed Timer\",\"type\":\"timer\",\"configuration\":{\"timerFormat\":\"long\",\"timezone\":\"UTC\",\"timerState\":\"stopped\"},\"modified\":1640403543115,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403543115},\"15631105-127c-44a0-8d54-e3800943a98b\":{\"identifier\":{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},\"name\":\"Notebook\",\"type\":\"notebook\",\"configuration\":{\"defaultSort\":\"oldest\",\"entries\":{},\"imageMigrationVer\":\"v1\",\"pageTitle\":\"Page\",\"sections\":[{\"id\":\"ef4092ba-b6d1-4275-a659-bfae80b6ec9a\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Section\",\"pages\":[{\"id\":\"b187e90c-4759-47d5-af16-4a347520c0a6\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Page\",\"pageTitle\":\"Page\"}],\"sectionTitle\":\"Section\"}],\"sectionTitle\":\"Section\",\"type\":\"General\"},\"modified\":1640403544367,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403544368},\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\":{\"name\":\"Mega Display Layout\",\"type\":\"layout\",\"identifier\":{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"},\"composition\":[],\"configuration\":{\"items\":[],\"layoutGrid\":[10,10]},\"modified\":1640403603545,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403603545}}","mct-tree-expanded":"[]"}
|
||||
@@ -5,7 +5,7 @@
|
||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||
const config = {
|
||||
retries: 0,
|
||||
testDir: 'tests',
|
||||
testDir: 'tests/visual',
|
||||
timeout: 90 * 1000,
|
||||
workers: 1,
|
||||
webServer: {
|
||||
@@ -26,7 +26,6 @@ const config = {
|
||||
reporter: [
|
||||
['list'],
|
||||
['junit', { outputFile: 'test-results/results.xml' }],
|
||||
['allure-playwright']
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
122
e2e/tests/generateLocalStorage.e2e.spec.js
Normal file
122
e2e/tests/generateLocalStorage.e2e.spec.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is used for generating localStorage artifacts for visual and performance
|
||||
tests. This must be generated against app.js
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const fs = require('fs');
|
||||
|
||||
test('Generate domainObjects and store localstorage as localstorage.json', async ({ page }) => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click :nth-match(:text("Folder"), 2)
|
||||
await page.click(':nth-match(:text("Folder"), 2)');
|
||||
|
||||
// Fill text=Properties Title Notes >> input[type="text"]
|
||||
await page.fill('text=Properties Title Notes >> input[type="text"]', 'All DomainObjects');
|
||||
//await page.fill('input[type="text"]', 'All DomainObjects');
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=grid' }*/),
|
||||
page.click('text=OK')
|
||||
]);
|
||||
|
||||
// Click button:has-text("Create")
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Timer
|
||||
await page.click('text=Timer');
|
||||
|
||||
// Click text=Save In My Items All DomainObjects >> input[type="search"]
|
||||
await page.click('text=Save In My Items All DomainObjects >> input[type="search"]');
|
||||
|
||||
// Fill text=Save In My Items All DomainObjects >> input[type="search"]
|
||||
await page.fill('text=Save In My Items All DomainObjects >> input[type="search"]', 'All DomainObjects');
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/810cc308-76d6-4e64-ab85-cb543c655698?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=timer.view' }*/),
|
||||
page.click('text=OK')
|
||||
]);
|
||||
|
||||
// Click button:has-text("Create")
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Notebook
|
||||
await page.click('text=Notebook');
|
||||
|
||||
// Fill text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]
|
||||
await page.fill('text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]', 'Notebook');
|
||||
|
||||
// Click text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
|
||||
//await page.click('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]');
|
||||
|
||||
// Fill text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
|
||||
await page.fill('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]', 'All DomainObjects');
|
||||
|
||||
// Click ul:nth-child(3) .c-tree__item-h .c-tree__item
|
||||
await page.click('ul:nth-child(3) .c-tree__item-h .c-tree__item');
|
||||
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/ROOT/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/6b10425e-2d7f-4f65-83de-35317fbc2493?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=notebook-vue&pageId=9ad5b4ea-8dec-4cc0-b303-43a4f50ac7fa§ionId=d298175b-d715-426e-8036-b87056e14525' }*/),
|
||||
page.click('button:has-text("OK")')
|
||||
]);
|
||||
|
||||
await page.pause();
|
||||
|
||||
const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage));
|
||||
fs.writeFileSync('e2e/localstorage.json', localStorage);
|
||||
});
|
||||
|
||||
test('Load localstorage.json and verify contents', async ({ page }) => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
|
||||
|
||||
const deserializedStorage = JSON.parse(localStorage)
|
||||
await page.evaluate(deserializedStorage => {
|
||||
for (const key in deserializedStorage) {
|
||||
localStorage.setItem(key, deserializedStorage[key]);
|
||||
}
|
||||
}, deserializedStorage);
|
||||
|
||||
await page.reload();
|
||||
|
||||
// Expand Default Folder
|
||||
await page.click('a:has-text("All DomainObjects Folder")');
|
||||
|
||||
await expect(page.locator('a:has-text("Unnamed Timer Timer")')).toBeEnabled();
|
||||
await expect(page.locator('a:has-text("Notebook Notebook")')).toBeEnabled();
|
||||
|
||||
});
|
||||
@@ -44,6 +44,5 @@ test('Verify that the create button appears and that the Folder Domain Object is
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Verify that Create Folder appears in the dropdown
|
||||
const locator = page.locator(':nth-match(:text("Folder"), 2)');
|
||||
await expect(locator).toBeEnabled();
|
||||
await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();
|
||||
});
|
||||
@@ -36,12 +36,13 @@ const { test, expect } = require('@playwright/test');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
const fs = require('fs');
|
||||
|
||||
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
|
||||
const VISUAL_GRACE_PERIOD = 1 * 1000; //Lets the application "simmer" before the snapshot is taken
|
||||
|
||||
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
|
||||
// Will replace with cy.clock() equivalent
|
||||
test.beforeEach(async ({ context }) => {
|
||||
test.beforeEach(async ({ context,page }) => {
|
||||
await context.addInitScript({
|
||||
// eslint-disable-next-line no-undef
|
||||
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
|
||||
@@ -49,11 +50,10 @@ test.beforeEach(async ({ context }) => {
|
||||
await context.addInitScript(() => {
|
||||
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
|
||||
});
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
test('Visual - Root and About', async ({ page }) => {
|
||||
// Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Verify that Create button is actionable
|
||||
const createButtonLocator = page.locator('button:has-text("Create")');
|
||||
@@ -77,8 +77,6 @@ test('Visual - Root and About', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('Visual - Default Condition Set', async ({ page }) => {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
@@ -95,8 +93,6 @@ test('Visual - Default Condition Set', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('Visual - Default Condition Widget', async ({ page }) => {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
@@ -111,3 +107,35 @@ test('Visual - Default Condition Widget', async ({ page }) => {
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Condition Widget');
|
||||
});
|
||||
|
||||
test('Visual - Default Timer', async ({ page }) => {
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Timer
|
||||
await page.click('text=Timer');
|
||||
|
||||
// Click text=OK
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Condition Widget object
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Timer');
|
||||
});
|
||||
|
||||
test('Visual - Default Display Layout', async ({ page }) => {
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Timer
|
||||
await page.click('text=Display Layout');
|
||||
|
||||
// Click text=OK
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Condition Widget object
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Display Layout');
|
||||
});
|
||||
82
e2e/tests/visual/localStorage.visual.spec.js
Normal file
82
e2e/tests/visual/localStorage.visual.spec.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
Collection of Visual Tests set to run from a pre-generated . The tests within this suite
|
||||
are only meant to run against openmct's app.js started by `npm run start` within the
|
||||
`./e2e/playwright-visual.config.js` file.
|
||||
|
||||
These should only use functional expect statements to verify assumptions about the state
|
||||
in a test and not for functional verification of correctness. Visual tests are not supposed
|
||||
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
|
||||
|
||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
const fs = require('fs');
|
||||
|
||||
const VISUAL_GRACE_PERIOD = 1 * 1000; //Lets the application "simmer" before the snapshot is taken
|
||||
|
||||
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
|
||||
// Will replace with cy.clock() equivalent
|
||||
test.beforeEach(async ({ context,page }) => {
|
||||
await context.addInitScript({
|
||||
// eslint-disable-next-line no-undef
|
||||
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
|
||||
});
|
||||
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
|
||||
|
||||
const deserializedStorage = JSON.parse(localStorage)
|
||||
await page.evaluate(deserializedStorage => {
|
||||
for (const key in deserializedStorage) {
|
||||
localStorage.setItem(key, deserializedStorage[key]);
|
||||
}
|
||||
}, deserializedStorage);
|
||||
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('Visual - Combined Display Layout', async ({ page }) => {
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Timer
|
||||
await page.click('text=Display Layout');
|
||||
|
||||
// Click text=OK
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Condition Widget object
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Display Layout');
|
||||
});
|
||||
@@ -75,6 +75,11 @@
|
||||
const TWO_HOURS = ONE_HOUR * 2;
|
||||
const ONE_DAY = ONE_HOUR * 24;
|
||||
|
||||
[
|
||||
'example/eventGenerator'
|
||||
].forEach(
|
||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
||||
);
|
||||
|
||||
openmct.install(openmct.plugins.LocalStorage());
|
||||
|
||||
|
||||
11
package.json
11
package.json
@@ -6,7 +6,7 @@
|
||||
"@braintree/sanitize-url": "^5.0.2",
|
||||
"@percy/cli": "^1.0.0-beta.70",
|
||||
"@percy/playwright": "^1.0.1",
|
||||
"@playwright/test": "^1.16.3",
|
||||
"@playwright/test": "^1.17.1",
|
||||
"allure-playwright": "^2.0.0-beta.14",
|
||||
"angular": ">=1.8.0",
|
||||
"angular-route": "1.4.14",
|
||||
@@ -34,7 +34,7 @@
|
||||
"html2canvas": "^1.0.0-rc.7",
|
||||
"imports-loader": "^0.8.0",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jasmine-core": "^4.0.0",
|
||||
"jasmine-core": "^3.7.1",
|
||||
"jsdoc": "^3.3.2",
|
||||
"karma": "6.3.9",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
@@ -58,7 +58,7 @@
|
||||
"moment-timezone": "0.5.28",
|
||||
"node-bourbon": "^4.2.3",
|
||||
"painterro": "^1.2.56",
|
||||
"playwright": "^1.16.3",
|
||||
"playwright": "^1.17.1",
|
||||
"plotly.js-basic-dist": "^2.5.0",
|
||||
"plotly.js-gl2d-dist": "^2.5.0",
|
||||
"printj": "^1.2.1",
|
||||
@@ -74,7 +74,7 @@
|
||||
"v8-compile-cache": "^1.1.0",
|
||||
"vue": "2.5.6",
|
||||
"vue-eslint-parser": "8.0.1",
|
||||
"vue-loader": "15.9.8",
|
||||
"vue-loader": "^15.2.6",
|
||||
"vue-template-compiler": "2.5.6",
|
||||
"webpack": "^5.53.0",
|
||||
"webpack-cli": "^4.0.0",
|
||||
@@ -97,8 +97,9 @@
|
||||
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
|
||||
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke",
|
||||
"test:e2e:generate": "npx playwright test --config=e2e/playwright-local.config.js generateLocalStorage",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
|
||||
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default",
|
||||
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js",
|
||||
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
||||
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
|
||||
"verify": "concurrently 'npm:test' 'npm:lint'",
|
||||
|
||||
141
src/MCT.js
141
src/MCT.js
@@ -22,17 +22,24 @@
|
||||
|
||||
define([
|
||||
'EventEmitter',
|
||||
'./BundleRegistry',
|
||||
'./installDefaultBundles',
|
||||
'./api/api',
|
||||
'./api/overlays/OverlayAPI',
|
||||
'./selection/Selection',
|
||||
'objectUtils',
|
||||
'./plugins/plugins',
|
||||
'./adapter/indicators/legacy-indicators-plugin',
|
||||
'./ui/registries/ViewRegistry',
|
||||
'./plugins/imagery/plugin',
|
||||
'./ui/registries/InspectorViewRegistry',
|
||||
'./ui/registries/ToolbarRegistry',
|
||||
'./ui/router/ApplicationRouter',
|
||||
'./ui/router/Browse',
|
||||
'../platform/framework/src/Main',
|
||||
'./ui/layout/Layout.vue',
|
||||
'../platform/core/src/objects/DomainObjectImpl',
|
||||
'../platform/core/src/capabilities/ContextualDomainObject',
|
||||
'./ui/preview/plugin',
|
||||
'./api/Branding',
|
||||
'./plugins/licenses/plugin',
|
||||
@@ -45,17 +52,24 @@ define([
|
||||
'vue'
|
||||
], function (
|
||||
EventEmitter,
|
||||
BundleRegistry,
|
||||
installDefaultBundles,
|
||||
api,
|
||||
OverlayAPI,
|
||||
Selection,
|
||||
objectUtils,
|
||||
plugins,
|
||||
LegacyIndicatorsPlugin,
|
||||
ViewRegistry,
|
||||
ImageryPlugin,
|
||||
InspectorViewRegistry,
|
||||
ToolbarRegistry,
|
||||
ApplicationRouter,
|
||||
Browse,
|
||||
Main,
|
||||
Layout,
|
||||
DomainObjectImpl,
|
||||
ContextualDomainObject,
|
||||
PreviewPlugin,
|
||||
BrandingAPI,
|
||||
LicensesPlugin,
|
||||
@@ -92,6 +106,23 @@ define([
|
||||
revision: __OPENMCT_REVISION__,
|
||||
branch: __OPENMCT_BUILD_BRANCH__
|
||||
};
|
||||
/* eslint-enable no-undef */
|
||||
|
||||
this.legacyBundle = {
|
||||
extensions: {
|
||||
services: [
|
||||
{
|
||||
key: "openmct",
|
||||
implementation: function ($injector) {
|
||||
this.$injector = $injector;
|
||||
|
||||
return this;
|
||||
}.bind(this),
|
||||
depends: ['$injector']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
/**
|
||||
@@ -231,12 +262,16 @@ define([
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
|
||||
this.legacyRegistry = new BundleRegistry();
|
||||
installDefaultBundles(this.legacyRegistry);
|
||||
|
||||
// Plugins that are installed by default
|
||||
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.Chart());
|
||||
this.install(this.plugins.TelemetryTable.default());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LegacyIndicatorsPlugin());
|
||||
this.install(LicensesPlugin.default());
|
||||
this.install(RemoveActionPlugin.default());
|
||||
this.install(MoveActionPlugin.default());
|
||||
@@ -268,6 +303,51 @@ define([
|
||||
|
||||
MCT.prototype.MCT = MCT;
|
||||
|
||||
MCT.prototype.legacyExtension = function (category, extension) {
|
||||
this.legacyBundle.extensions[category] =
|
||||
this.legacyBundle.extensions[category] || [];
|
||||
this.legacyBundle.extensions[category].push(extension);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a legacy object, for compatibility purposes only. This method
|
||||
* will be deprecated and removed in the future.
|
||||
* @private
|
||||
*/
|
||||
MCT.prototype.legacyObject = function (domainObject) {
|
||||
let capabilityService = this.$injector.get('capabilityService');
|
||||
|
||||
function instantiate(model, keyString) {
|
||||
const capabilities = capabilityService.getCapabilities(model, keyString);
|
||||
model.id = keyString;
|
||||
|
||||
return new DomainObjectImpl(keyString, model, capabilities);
|
||||
}
|
||||
|
||||
if (Array.isArray(domainObject)) {
|
||||
// an array of domain objects. [object, ...ancestors] representing
|
||||
// a single object with a given chain of ancestors. We instantiate
|
||||
// as a single contextual domain object.
|
||||
return domainObject
|
||||
.map((o) => {
|
||||
let keyString = objectUtils.makeKeyString(o.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(o);
|
||||
|
||||
return instantiate(oldModel, keyString);
|
||||
})
|
||||
.reverse()
|
||||
.reduce((parent, child) => {
|
||||
return new ContextualDomainObject(child, parent);
|
||||
});
|
||||
|
||||
} else {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
|
||||
return instantiate(oldModel, keyString);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set path to where assets are hosted. This should be the path to main.js.
|
||||
* @memberof module:openmct.MCT#
|
||||
@@ -313,6 +393,25 @@ define([
|
||||
|
||||
this.element = domElement;
|
||||
|
||||
this.legacyExtension('runs', {
|
||||
depends: ['navigationService'],
|
||||
implementation: function (navigationService) {
|
||||
navigationService
|
||||
.addListener(this.emit.bind(this, 'navigation'));
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
// TODO: remove with legacy types.
|
||||
this.types.listKeys().forEach(function (typeKey) {
|
||||
const type = this.types.get(typeKey);
|
||||
const legacyDefinition = type.toLegacyDefinition();
|
||||
legacyDefinition.key = typeKey;
|
||||
this.legacyExtension('types', legacyDefinition);
|
||||
}.bind(this));
|
||||
|
||||
this.legacyRegistry.register('adapter', this.legacyBundle);
|
||||
this.legacyRegistry.enable('adapter');
|
||||
|
||||
this.router.route(/^\/$/, () => {
|
||||
this.router.setPath('/browse/');
|
||||
});
|
||||
@@ -323,27 +422,35 @@ define([
|
||||
* @event start
|
||||
* @memberof module:openmct.MCT~
|
||||
*/
|
||||
const startPromise = new Main();
|
||||
startPromise.run(this)
|
||||
.then(function (angular) {
|
||||
this.$angular = angular;
|
||||
// OpenMCT Object provider doesn't operate properly unless
|
||||
// something has depended upon objectService. Cool, right?
|
||||
this.$injector.get('objectService');
|
||||
|
||||
if (!isHeadlessMode) {
|
||||
const appLayout = new Vue({
|
||||
components: {
|
||||
'Layout': Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: this
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
if (!isHeadlessMode) {
|
||||
const appLayout = new Vue({
|
||||
components: {
|
||||
'Layout': Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: this
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
|
||||
this.layout = appLayout.$refs.layout;
|
||||
Browse(this);
|
||||
}
|
||||
this.layout = appLayout.$refs.layout;
|
||||
Browse(this);
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
MCT.prototype.startHeadless = function () {
|
||||
|
||||
@@ -22,18 +22,21 @@
|
||||
|
||||
define([
|
||||
'./plugins/plugins',
|
||||
'legacyRegistry',
|
||||
'utils/testing'
|
||||
], function (plugins, testUtils) {
|
||||
], function (plugins, legacyRegistry, testUtils) {
|
||||
describe("MCT", function () {
|
||||
let openmct;
|
||||
let mockPlugin;
|
||||
let mockPlugin2;
|
||||
let mockListener;
|
||||
let oldBundles;
|
||||
|
||||
beforeEach(function () {
|
||||
mockPlugin = jasmine.createSpy('plugin');
|
||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
||||
mockListener = jasmine.createSpy('listener');
|
||||
oldBundles = legacyRegistry.list();
|
||||
|
||||
openmct = testUtils.createOpenMct();
|
||||
|
||||
@@ -44,6 +47,12 @@ define([
|
||||
|
||||
// Clean up the dirty singleton.
|
||||
afterEach(function () {
|
||||
legacyRegistry.list().forEach(function (bundle) {
|
||||
if (oldBundles.indexOf(bundle) === -1) {
|
||||
legacyRegistry.delete(bundle);
|
||||
}
|
||||
});
|
||||
|
||||
return testUtils.resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
@@ -102,6 +111,10 @@ define([
|
||||
describe("setAssetPath", function () {
|
||||
let testAssetPath;
|
||||
|
||||
beforeEach(function () {
|
||||
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
|
||||
});
|
||||
|
||||
it("configures the path for assets", function () {
|
||||
testAssetPath = "some/path/";
|
||||
openmct.setAssetPath(testAssetPath);
|
||||
|
||||
@@ -133,9 +133,5 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
CompositionAPI.prototype.supportsComposition = function (domainObject) {
|
||||
return this.get(domainObject) !== undefined;
|
||||
};
|
||||
|
||||
return CompositionAPI;
|
||||
});
|
||||
|
||||
@@ -87,12 +87,6 @@ define([
|
||||
expect(composition).toEqual(jasmine.any(CompositionCollection));
|
||||
});
|
||||
|
||||
it('correctly reflects composability', function () {
|
||||
expect(compositionAPI.supportsComposition(domainObject)).toBe(true);
|
||||
delete domainObject.composition;
|
||||
expect(compositionAPI.supportsComposition(domainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it('loads composition from domain object', function () {
|
||||
const listener = jasmine.createSpy('addListener');
|
||||
composition.on('add', listener);
|
||||
|
||||
@@ -49,10 +49,8 @@ define([
|
||||
this.onMutation = this.onMutation.bind(this);
|
||||
|
||||
this.cannotContainItself = this.cannotContainItself.bind(this);
|
||||
this.supportsComposition = this.supportsComposition.bind(this);
|
||||
|
||||
compositionAPI.addPolicy(this.cannotContainItself);
|
||||
compositionAPI.addPolicy(this.supportsComposition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,13 +61,6 @@ define([
|
||||
&& parent.identifier.key === child.identifier.key);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
DefaultCompositionProvider.prototype.supportsComposition = function (parent, child) {
|
||||
return this.publicAPI.composition.supportsComposition(parent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this provider should be used to load composition for a
|
||||
* particular domain object.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
|
||||
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
|
||||
import CheckBoxField from './components/controls/CheckBoxField.vue';
|
||||
import Datetime from './components/controls/Datetime.vue';
|
||||
import FileInput from './components/controls/FileInput.vue';
|
||||
import Locator from './components/controls/Locator.vue';
|
||||
@@ -13,7 +12,6 @@ import Vue from 'vue';
|
||||
|
||||
export const DEFAULT_CONTROLS_MAP = {
|
||||
'autocomplete': AutoCompleteField,
|
||||
'checkbox': CheckBoxField,
|
||||
'composite': ClockDisplayFormatField,
|
||||
'datetime': Datetime,
|
||||
'file-input': FileInput,
|
||||
|
||||
@@ -172,9 +172,7 @@ export default class FormsAPI {
|
||||
|
||||
function onFormSave(save) {
|
||||
return () => {
|
||||
if (overlay) {
|
||||
overlay.dismiss();
|
||||
}
|
||||
overlay.dismiss();
|
||||
|
||||
if (save) {
|
||||
save(changes);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@@ -24,60 +24,49 @@
|
||||
<div class="c-form">
|
||||
<div class="c-overlay__top-bar c-form__top-bar">
|
||||
<div class="c-overlay__dialog-title">{{ model.title }}</div>
|
||||
<div
|
||||
v-if="hasRequiredFields"
|
||||
class="c-overlay__dialog-hint hint"
|
||||
>All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||
</div>
|
||||
<form
|
||||
name="mctForm"
|
||||
class="c-form__contents"
|
||||
autocomplete="off"
|
||||
@submit.prevent
|
||||
<form name="mctForm"
|
||||
class="c-form__contents"
|
||||
autocomplete="off"
|
||||
@submit.prevent
|
||||
>
|
||||
<div
|
||||
v-for="section in formSections"
|
||||
:key="section.id"
|
||||
class="c-form__section"
|
||||
:class="section.cssClass"
|
||||
<div v-for="section in formSections"
|
||||
:key="section.id"
|
||||
class="c-form__section"
|
||||
:class="section.cssClass"
|
||||
>
|
||||
<h2
|
||||
v-if="section.name"
|
||||
<h2 v-if="section.name"
|
||||
class="c-form__section-header"
|
||||
>
|
||||
{{ section.name }}
|
||||
</h2>
|
||||
<div
|
||||
v-for="(row, index) in section.rows"
|
||||
:key="row.id"
|
||||
class="u-contents"
|
||||
<div v-for="(row, index) in section.rows"
|
||||
:key="row.id"
|
||||
class="u-contents"
|
||||
>
|
||||
<FormRow
|
||||
:css-class="section.cssClass"
|
||||
:first="index < 1"
|
||||
:row="row"
|
||||
@onChange="onChange"
|
||||
<FormRow :css-class="section.cssClass"
|
||||
:first="index < 1"
|
||||
:row="row"
|
||||
@onChange="onChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
|
||||
<button
|
||||
tabindex="0"
|
||||
:disabled="isInvalid"
|
||||
class="c-button c-button--major"
|
||||
@click="onSave"
|
||||
<button tabindex="0"
|
||||
:disabled="isInvalid"
|
||||
class="c-button c-button--major"
|
||||
@click="onSave"
|
||||
>
|
||||
{{ submitLabel }}
|
||||
OK
|
||||
</button>
|
||||
<button
|
||||
v-if="!hideCancel"
|
||||
tabindex="0"
|
||||
class="c-button"
|
||||
@click="onDismiss"
|
||||
<button tabindex="0"
|
||||
class="c-button"
|
||||
@click="onDismiss"
|
||||
>
|
||||
{{ cancelLabel }}
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,42 +100,11 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasRequiredFields() {
|
||||
return this.model.sections.some(section =>
|
||||
section.rows.some(row => row.required));
|
||||
},
|
||||
isInvalid() {
|
||||
return Object.entries(this.invalidProperties)
|
||||
.some(([key, value]) => {
|
||||
return value;
|
||||
});
|
||||
},
|
||||
submitLabel() {
|
||||
if (
|
||||
this.model.buttons
|
||||
&& this.model.buttons.submit
|
||||
&& this.model.buttons.submit.label
|
||||
) {
|
||||
return this.model.buttons.submit.label;
|
||||
}
|
||||
|
||||
return 'OK';
|
||||
},
|
||||
cancelLabel() {
|
||||
if (
|
||||
this.model.buttons
|
||||
&& this.model.buttons.cancel
|
||||
&& this.model.buttons.cancel.label
|
||||
) {
|
||||
return this.model.buttons.submit.label;
|
||||
}
|
||||
|
||||
return 'Cancel';
|
||||
},
|
||||
hideCancel() {
|
||||
return this.model.buttons
|
||||
&& this.model.buttons.cancel
|
||||
&& this.model.buttons.cancel.hide === true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -75,12 +75,10 @@ export default {
|
||||
rowClass() {
|
||||
let cssClass = this.cssClass;
|
||||
|
||||
if (!this.row.required) {
|
||||
return;
|
||||
if (this.row.required) {
|
||||
cssClass = `${cssClass} req`;
|
||||
}
|
||||
|
||||
cssClass = `${cssClass} req`;
|
||||
|
||||
if (this.visited && this.valid !== undefined) {
|
||||
if (this.valid === true) {
|
||||
cssClass = `${cssClass} valid`;
|
||||
|
||||
@@ -1,62 +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>
|
||||
<span class="form-control shell">
|
||||
<span
|
||||
class="field control"
|
||||
:class="model.cssClass"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isChecked"
|
||||
@input="toggleCheckBox"
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isChecked: this.model.value
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleCheckBox() {
|
||||
this.isChecked = !this.isChecked;
|
||||
const data = {
|
||||
model: this.model,
|
||||
value: this.isChecked
|
||||
};
|
||||
this.$emit('onChange', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@@ -36,14 +36,13 @@ class InMemorySearchProvider {
|
||||
*/
|
||||
this.MAX_CONCURRENT_REQUESTS = 100;
|
||||
/**
|
||||
* If max results is not specified in query, use this as default.
|
||||
*/
|
||||
* If max results is not specified in query, use this as default.
|
||||
*/
|
||||
this.DEFAULT_MAX_RESULTS = 100;
|
||||
|
||||
this.openmct = openmct;
|
||||
|
||||
this.indexedIds = {};
|
||||
this.indexedCompositions = {};
|
||||
this.idsToIndex = [];
|
||||
this.pendingIndex = {};
|
||||
this.pendingRequests = 0;
|
||||
@@ -59,6 +58,7 @@ class InMemorySearchProvider {
|
||||
this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
|
||||
this.onerror = this.onWorkerError.bind(this);
|
||||
this.startIndexing = this.startIndexing.bind(this);
|
||||
this.onMutationOfIndexedObject = this.onMutationOfIndexedObject.bind(this);
|
||||
|
||||
this.openmct.on('start', this.startIndexing);
|
||||
this.openmct.on('destroy', () => {
|
||||
@@ -68,9 +68,6 @@ class InMemorySearchProvider {
|
||||
this.worker.port.onmessageerror = null;
|
||||
this.worker.port.close();
|
||||
}
|
||||
|
||||
this.destroyObservers(this.indexedIds);
|
||||
this.destroyObservers(this.indexedCompositions);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,7 +137,7 @@ class InMemorySearchProvider {
|
||||
};
|
||||
modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
|
||||
const identifier = this.openmct.objects.parseKeyString(hit.keyString);
|
||||
const domainObject = await this.openmct.objects.get(identifier);
|
||||
const domainObject = await this.openmct.objects.get(identifier.key);
|
||||
|
||||
return domainObject;
|
||||
}));
|
||||
@@ -216,52 +213,29 @@ class InMemorySearchProvider {
|
||||
}
|
||||
}
|
||||
|
||||
onNameMutation(domainObject, name) {
|
||||
onMutationOfIndexedObject(domainObject) {
|
||||
const provider = this;
|
||||
|
||||
domainObject.name = name;
|
||||
provider.index(domainObject);
|
||||
}
|
||||
|
||||
onCompositionMutation(domainObject, composition) {
|
||||
const provider = this;
|
||||
const indexedComposition = domainObject.composition;
|
||||
const identifiersToIndex = composition
|
||||
.filter(identifier => !indexedComposition
|
||||
.some(indexedIdentifier => this.openmct.objects
|
||||
.areIdsEqual([identifier, indexedIdentifier])));
|
||||
|
||||
identifiersToIndex.forEach(identifier => {
|
||||
this.openmct.objects.get(identifier).then(objectToIndex => provider.index(objectToIndex));
|
||||
});
|
||||
provider.index(domainObject.identifier, domainObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a domainObject to the worker to be indexed.
|
||||
* If the object has composition, schedule those ids for later indexing.
|
||||
* Watch for object changes and re-index object and children if so
|
||||
* Pass an id and model to the worker to be indexed. If the model has
|
||||
* composition, schedule those ids for later indexing.
|
||||
*
|
||||
* @private
|
||||
* @param domainObject a domainObject
|
||||
* @param id a model id
|
||||
* @param model a model
|
||||
*/
|
||||
async index(domainObject) {
|
||||
async index(id, domainObject) {
|
||||
const provider = this;
|
||||
const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
const keyString = this.openmct.objects.makeKeyString(id);
|
||||
if (!this.indexedIds[keyString]) {
|
||||
this.indexedIds[keyString] = this.openmct.objects.observe(
|
||||
domainObject,
|
||||
'name',
|
||||
this.onNameMutation.bind(this, domainObject)
|
||||
);
|
||||
this.indexedCompositions[keyString] = this.openmct.objects.observe(
|
||||
domainObject,
|
||||
'composition',
|
||||
this.onCompositionMutation.bind(this, domainObject)
|
||||
);
|
||||
this.openmct.objects.observe(domainObject, `*`, this.onMutationOfIndexedObject);
|
||||
}
|
||||
|
||||
if ((keyString !== 'ROOT')) {
|
||||
this.indexedIds[keyString] = true;
|
||||
|
||||
if ((id.key !== 'ROOT')) {
|
||||
if (this.worker) {
|
||||
this.worker.port.postMessage({
|
||||
request: 'index',
|
||||
@@ -273,12 +247,15 @@ class InMemorySearchProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const composition = this.openmct.composition.get(domainObject);
|
||||
const composition = this.openmct.composition.registry.find(foundComposition => {
|
||||
return foundComposition.appliesTo(domainObject);
|
||||
});
|
||||
|
||||
if (composition !== undefined) {
|
||||
const children = await composition.load();
|
||||
|
||||
children.forEach(child => provider.scheduleForIndexing(child.identifier));
|
||||
if (composition) {
|
||||
const childIdentifiers = await composition.load(domainObject);
|
||||
childIdentifiers.forEach(function (childIdentifier) {
|
||||
provider.scheduleForIndexing(childIdentifier);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,12 +271,12 @@ class InMemorySearchProvider {
|
||||
const provider = this;
|
||||
|
||||
this.pendingRequests += 1;
|
||||
const domainObject = await this.openmct.objects.get(keyString);
|
||||
const identifier = await this.openmct.objects.parseKeyString(keyString);
|
||||
const domainObject = await this.openmct.objects.get(identifier.key);
|
||||
delete provider.pendingIndex[keyString];
|
||||
|
||||
try {
|
||||
if (domainObject) {
|
||||
await provider.index(domainObject);
|
||||
await provider.index(identifier, domainObject);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to index domain object ' + keyString, error);
|
||||
@@ -328,9 +305,9 @@ class InMemorySearchProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* A local version of the same SharedWorker function
|
||||
* if we don't have SharedWorkers available (e.g., iOS)
|
||||
*/
|
||||
* A local version of the same SharedWorker function
|
||||
* if we don't have SharedWorkers available (e.g., iOS)
|
||||
*/
|
||||
localIndexItem(keyString, model) {
|
||||
this.localIndexedItems[keyString] = {
|
||||
type: model.type,
|
||||
@@ -370,16 +347,6 @@ class InMemorySearchProvider {
|
||||
};
|
||||
this.onWorkerMessage(eventToReturn);
|
||||
}
|
||||
|
||||
destroyObservers(observers) {
|
||||
Object.entries(observers).forEach(([keyString, unobserve]) => {
|
||||
if (typeof unobserve === 'function') {
|
||||
unobserve();
|
||||
}
|
||||
|
||||
delete observers[keyString];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default InMemorySearchProvider;
|
||||
|
||||
@@ -33,10 +33,8 @@
|
||||
|
||||
port.onmessage = function (event) {
|
||||
if (event.data.request === 'index') {
|
||||
console.log('onmessage index: ', event.data);
|
||||
indexItem(event.data.keyString, event.data.model);
|
||||
} else if (event.data.request === 'search') {
|
||||
console.log('onmessage search: ', event.data);
|
||||
port.postMessage(search(event.data));
|
||||
}
|
||||
};
|
||||
@@ -79,8 +77,6 @@
|
||||
queryId: data.queryId
|
||||
};
|
||||
|
||||
console.log('indexed on search: ', indexedItems);
|
||||
|
||||
results = Object.values(indexedItems).filter((indexedItem) => {
|
||||
return indexedItem.name.toLowerCase().includes(input);
|
||||
});
|
||||
|
||||
@@ -55,11 +55,6 @@ define([
|
||||
*/
|
||||
function parseKeyString(keyString) {
|
||||
if (isIdentifier(keyString)) {
|
||||
// hack to workaround a bug mashing keyString into identifier.key
|
||||
if (!keyString.namespace && keyString.key.includes(':')) {
|
||||
return parseKeyString(keyString.key);
|
||||
}
|
||||
|
||||
return keyString;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import EventEmitter from 'EventEmitter';
|
||||
|
||||
const ERRORS = {
|
||||
TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
|
||||
TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
|
||||
LOADED: 'Telemetry Collection has already been loaded.'
|
||||
};
|
||||
|
||||
@@ -267,10 +266,6 @@ export class TelemetryCollection extends EventEmitter {
|
||||
this.lastBounds = bounds;
|
||||
|
||||
if (isTick) {
|
||||
if (this.timeKey === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// need to check futureBuffer and need to check
|
||||
// if anything has fallen out of bounds
|
||||
let startIndex = 0;
|
||||
@@ -310,6 +305,7 @@ export class TelemetryCollection extends EventEmitter {
|
||||
if (added.length > 0) {
|
||||
this.emit('add', added);
|
||||
}
|
||||
|
||||
} else {
|
||||
// user bounds change, reset
|
||||
this._reset();
|
||||
@@ -329,14 +325,12 @@ export class TelemetryCollection extends EventEmitter {
|
||||
let domains = this.metadata.valuesForHints(['domain']);
|
||||
let domain = domains.find((d) => d.key === timeSystem.key);
|
||||
|
||||
if (domain !== undefined) {
|
||||
// timeKey is used to create a dummy datum used for sorting
|
||||
this.timeKey = domain.source;
|
||||
} else {
|
||||
this._warn(ERRORS.TIMESYSTEM_KEY);
|
||||
this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION)
|
||||
if (domain === undefined) {
|
||||
this._error(ERRORS.TIMESYSTEM_KEY);
|
||||
}
|
||||
|
||||
|
||||
// timeKey is used to create a dummy datum used for sorting
|
||||
this.timeKey = domain.source; // this defaults to key if no source is set
|
||||
let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
|
||||
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||
|
||||
@@ -406,8 +400,4 @@ export class TelemetryCollection extends EventEmitter {
|
||||
_error(message) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
_warn(message) {
|
||||
console.warn(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,42 +43,42 @@ const DEFAULTS = [
|
||||
];
|
||||
|
||||
define([
|
||||
'../../adapter/bundle',
|
||||
'../../../example/eventGenerator/bundle',
|
||||
'../../../example/export/bundle',
|
||||
'../../../example/forms/bundle',
|
||||
'../../../example/identity/bundle',
|
||||
'../../../example/mobile/bundle',
|
||||
'../../../example/msl/bundle',
|
||||
'../../../example/notifications/bundle',
|
||||
'../../../example/persistence/bundle',
|
||||
'../../../example/policy/bundle',
|
||||
'../../../example/profiling/bundle',
|
||||
'../../../example/scratchpad/bundle',
|
||||
'../../../example/styleguide/bundle',
|
||||
'../../../platform/commonUI/browse/bundle',
|
||||
'../../../platform/commonUI/dialog/bundle',
|
||||
'../../../platform/commonUI/edit/bundle',
|
||||
'../../../platform/commonUI/general/bundle',
|
||||
'../../../platform/commonUI/inspect/bundle',
|
||||
'../../../platform/commonUI/mobile/bundle',
|
||||
'../../../platform/commonUI/notification/bundle',
|
||||
'../../../platform/commonUI/regions/bundle',
|
||||
'../../../platform/containment/bundle',
|
||||
'../../../platform/core/bundle',
|
||||
'../../../platform/entanglement/bundle',
|
||||
'../../../platform/exporters/bundle',
|
||||
'../../../platform/features/static-markup/bundle',
|
||||
'../../../platform/framework/bundle',
|
||||
'../../../platform/framework/src/load/Bundle',
|
||||
'../../../platform/identity/bundle',
|
||||
'../../../platform/persistence/aggregator/bundle',
|
||||
'../../../platform/persistence/elastic/bundle',
|
||||
'../../../platform/persistence/queue/bundle',
|
||||
'../../../platform/policy/bundle',
|
||||
'../../../platform/representation/bundle',
|
||||
'../../../platform/status/bundle',
|
||||
'../../../platform/telemetry/bundle'
|
||||
'../src/adapter/bundle',
|
||||
'../example/eventGenerator/bundle',
|
||||
'../example/export/bundle',
|
||||
'../example/forms/bundle',
|
||||
'../example/identity/bundle',
|
||||
'../example/mobile/bundle',
|
||||
'../example/msl/bundle',
|
||||
'../example/notifications/bundle',
|
||||
'../example/persistence/bundle',
|
||||
'../example/policy/bundle',
|
||||
'../example/profiling/bundle',
|
||||
'../example/scratchpad/bundle',
|
||||
'../example/styleguide/bundle',
|
||||
'../platform/commonUI/browse/bundle',
|
||||
'../platform/commonUI/dialog/bundle',
|
||||
'../platform/commonUI/edit/bundle',
|
||||
'../platform/commonUI/general/bundle',
|
||||
'../platform/commonUI/inspect/bundle',
|
||||
'../platform/commonUI/mobile/bundle',
|
||||
'../platform/commonUI/notification/bundle',
|
||||
'../platform/commonUI/regions/bundle',
|
||||
'../platform/containment/bundle',
|
||||
'../platform/core/bundle',
|
||||
'../platform/entanglement/bundle',
|
||||
'../platform/exporters/bundle',
|
||||
'../platform/features/static-markup/bundle',
|
||||
'../platform/framework/bundle',
|
||||
'../platform/framework/src/load/Bundle',
|
||||
'../platform/identity/bundle',
|
||||
'../platform/persistence/aggregator/bundle',
|
||||
'../platform/persistence/elastic/bundle',
|
||||
'../platform/persistence/queue/bundle',
|
||||
'../platform/policy/bundle',
|
||||
'../platform/representation/bundle',
|
||||
'../platform/status/bundle',
|
||||
'../platform/telemetry/bundle'
|
||||
], function () {
|
||||
const LEGACY_BUNDLES = Array.from(arguments);
|
||||
|
||||
@@ -31,7 +31,7 @@ export default class LADTableViewProvider {
|
||||
}
|
||||
|
||||
canView(domainObject) {
|
||||
const supportsComposition = this.openmct.composition.supportsComposition(domainObject);
|
||||
const supportsComposition = this.openmct.composition.get(domainObject) !== undefined;
|
||||
const providesTelemetry = this.openmct.telemetry.isTelemetryObject(domainObject);
|
||||
|
||||
return domainObject.type === 'LadTable'
|
||||
|
||||
@@ -130,6 +130,14 @@ describe("the plugin", function () {
|
||||
let mockComposition;
|
||||
|
||||
beforeEach(async () => {
|
||||
const getFunc = openmct.$injector.get;
|
||||
spyOn(openmct.$injector, "get")
|
||||
.withArgs("exportImageService").and.returnValue({
|
||||
exportPNG: () => {},
|
||||
exportJPG: () => {}
|
||||
})
|
||||
.and.callFake(getFunc);
|
||||
|
||||
barGraphObject = {
|
||||
identifier: {
|
||||
namespace: "",
|
||||
|
||||
@@ -87,7 +87,6 @@ describe("Clock plugin:", () => {
|
||||
|
||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
|
||||
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
|
||||
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
|
||||
|
||||
const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
|
||||
clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
|
||||
|
||||
34
src/plugins/condition/ConditionSetViewPolicy.js
Normal file
34
src/plugins/condition/ConditionSetViewPolicy.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
function ConditionSetViewPolicy() {
|
||||
}
|
||||
|
||||
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
|
||||
if (domainObject.getModel().type === 'conditionSet') {
|
||||
return view.key === 'conditionSet.view';
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default ConditionSetViewPolicy;
|
||||
@@ -39,10 +39,8 @@ export default class ConditionSetViewProvider {
|
||||
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
|
||||
}
|
||||
|
||||
canEdit(domainObject, objectPath) {
|
||||
const isConditionSet = domainObject.type === 'conditionSet';
|
||||
|
||||
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
|
||||
canEdit(domainObject) {
|
||||
return domainObject.type === 'conditionSet';
|
||||
}
|
||||
|
||||
view(domainObject, objectPath) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
|
||||
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
|
||||
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
|
||||
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
|
||||
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
|
||||
import uuid from "uuid";
|
||||
|
||||
export default function ConditionPlugin() {
|
||||
@@ -54,8 +55,11 @@ export default function ConditionPlugin() {
|
||||
domainObject.telemetry = {};
|
||||
}
|
||||
});
|
||||
let compositionPolicy = new ConditionSetCompositionPolicy(openmct);
|
||||
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
|
||||
openmct.legacyExtension('policies', {
|
||||
category: 'view',
|
||||
implementation: ConditionSetViewPolicy
|
||||
});
|
||||
openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
|
||||
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
|
||||
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
|
||||
openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));
|
||||
|
||||
@@ -56,7 +56,7 @@ a.c-condition-widget {
|
||||
}
|
||||
|
||||
// When the widget is in the main view, center it in the space
|
||||
.l-shell__main-container > * > .c-condition-widget {
|
||||
.l-shell__main-container > .c-condition-widget {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
@@ -66,11 +66,10 @@ export default class CreateAction extends PropertiesAction {
|
||||
});
|
||||
|
||||
const parentDomainObject = parentDomainObjectPath[0];
|
||||
const namespace = parentDomainObject.identifier.namespace || parentDomainObject.key || '';
|
||||
|
||||
this.domainObject.modified = Date.now();
|
||||
this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
|
||||
this.domainObject.identifier.namespace = namespace;
|
||||
this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
|
||||
|
||||
// Show saving progress dialog
|
||||
let dialog = this.openmct.overlays.progressDialog({
|
||||
@@ -100,7 +99,6 @@ export default class CreateAction extends PropertiesAction {
|
||||
*/
|
||||
async _navigateAndEdit(domainObject, parentDomainObjectpath) {
|
||||
let objectPath;
|
||||
let self = this;
|
||||
if (parentDomainObjectpath) {
|
||||
objectPath = parentDomainObjectpath && [domainObject].concat(parentDomainObjectpath);
|
||||
} else {
|
||||
@@ -108,22 +106,17 @@ export default class CreateAction extends PropertiesAction {
|
||||
}
|
||||
|
||||
const url = '#/browse/' + objectPath
|
||||
.map(object => object && this.openmct.objects.makeKeyString(object.identifier))
|
||||
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
|
||||
.reverse()
|
||||
.join('/');
|
||||
|
||||
function editObject() {
|
||||
const objectView = self.openmct.objectViews.get(domainObject, objectPath)[0];
|
||||
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
|
||||
|
||||
if (canEdit) {
|
||||
self.openmct.editor.edit();
|
||||
}
|
||||
}
|
||||
|
||||
this.openmct.router.once('afterNavigation', editObject);
|
||||
|
||||
this.openmct.router.navigate(url);
|
||||
|
||||
const objectView = this.openmct.objectViews.get(domainObject, objectPath)[0];
|
||||
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
|
||||
if (canEdit) {
|
||||
this.openmct.editor.edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,8 +65,13 @@ export default {
|
||||
keyString: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
imageHistorySize() {
|
||||
return this.imageHistory.length;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imageHistory(newHistory, oldHistory) {
|
||||
imageHistorySize(newSize, oldSize) {
|
||||
this.updatePlotImagery();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -240,6 +240,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
imageHistorySize() {
|
||||
return this.imageHistory.length;
|
||||
},
|
||||
compassRoseSizingClasses() {
|
||||
let compassRoseSizingClasses = '';
|
||||
if (this.sizedImageDimensions.width < 300) {
|
||||
@@ -406,23 +409,19 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imageHistory: {
|
||||
handler(newHistory, oldHistory) {
|
||||
const newSize = newHistory.length;
|
||||
let imageIndex;
|
||||
if (this.focusedImageTimestamp !== undefined) {
|
||||
const foundImageIndex = this.imageHistory.findIndex(image => {
|
||||
return image.time === this.focusedImageTimestamp;
|
||||
});
|
||||
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
|
||||
} else {
|
||||
imageIndex = newSize > 0 ? newSize - 1 : undefined;
|
||||
}
|
||||
imageHistorySize(newSize, oldSize) {
|
||||
let imageIndex;
|
||||
if (this.focusedImageTimestamp !== undefined) {
|
||||
const foundImageIndex = this.imageHistory.findIndex(image => {
|
||||
return image.time === this.focusedImageTimestamp;
|
||||
});
|
||||
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
|
||||
} else {
|
||||
imageIndex = newSize > 0 ? newSize - 1 : undefined;
|
||||
}
|
||||
|
||||
this.setFocusedImage(imageIndex, false);
|
||||
this.scrollToRight();
|
||||
},
|
||||
deep: true
|
||||
this.setFocusedImage(imageIndex, false);
|
||||
this.scrollToRight();
|
||||
},
|
||||
focusedImageIndex() {
|
||||
this.trackDuration();
|
||||
@@ -511,6 +510,12 @@ export default {
|
||||
this.timeContext.off("clock", this.trackDuration);
|
||||
}
|
||||
},
|
||||
boundsChange(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.previousFocusedImage = this.focusedImage ? JSON.parse(JSON.stringify(this.focusedImage)) : undefined;
|
||||
this.requestHistory();
|
||||
}
|
||||
},
|
||||
expand() {
|
||||
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
@@ -685,32 +690,22 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.previousFocusedImage) {
|
||||
// determine if the previous image exists in the new bounds of imageHistory
|
||||
const matchIndex = this.matchIndexOfPreviousImage(
|
||||
this.previousFocusedImage,
|
||||
this.imageHistory
|
||||
);
|
||||
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
|
||||
|
||||
delete this.previousFocusedImage;
|
||||
}
|
||||
|
||||
if (thumbnailClick) {
|
||||
//We use the props till the user changes what they want to see
|
||||
this.focusedImageTimestamp = undefined;
|
||||
//set the previousFocusedImage when a user chooses an image
|
||||
this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
|
||||
}
|
||||
|
||||
if (this.previousFocusedImage) {
|
||||
// determine if the previous image exists in the new bounds of imageHistory
|
||||
if (!thumbnailClick) {
|
||||
const matchIndex = this.matchIndexOfPreviousImage(
|
||||
this.previousFocusedImage,
|
||||
this.imageHistory
|
||||
);
|
||||
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
|
||||
}
|
||||
|
||||
if (!(this.isPaused || thumbnailClick)
|
||||
|| focusedIndex === this.imageHistory.length - 1) {
|
||||
delete this.previousFocusedImage;
|
||||
}
|
||||
}
|
||||
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
|
||||
//TODO: do we even need this anymore?
|
||||
if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
|
||||
this.nextImageIndex = focusedIndex;
|
||||
//this could happen if bounds changes
|
||||
@@ -721,6 +716,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
|
||||
if (thumbnailClick && !this.isPaused) {
|
||||
this.paused(true);
|
||||
}
|
||||
|
||||
@@ -120,15 +120,9 @@ export default {
|
||||
return this.timeFormatter.parse(datum);
|
||||
},
|
||||
boundsChange(bounds, isTick) {
|
||||
if (isTick) {
|
||||
return;
|
||||
if (!isTick) {
|
||||
this.requestHistory();
|
||||
}
|
||||
|
||||
// forcibly reset the imageContainer size to prevent an aspect ratio distortion
|
||||
delete this.imageContainerWidth;
|
||||
delete this.imageContainerHeight;
|
||||
|
||||
return this.requestHistory();
|
||||
},
|
||||
async requestHistory() {
|
||||
let bounds = this.timeContext.bounds();
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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 installDefaultBundles from './installDefaultBundles';
|
||||
import BundleRegistry from './BundleRegistry';
|
||||
import Main from '../../../platform/framework/src/Main';
|
||||
import objectUtils from '../../api/objects/object-utils';
|
||||
import DomainObjectImpl from '../../../platform/core/src/objects/DomainObjectImpl';
|
||||
import ContextualDomainObject from '../../../platform/core/src/capabilities/ContextualDomainObject';
|
||||
|
||||
export default function LegacySupportPlugin() {
|
||||
return function install(openmct) {
|
||||
openmct.legacyBundle = {
|
||||
extensions: {
|
||||
services: [
|
||||
{
|
||||
key: "openmct",
|
||||
implementation: function ($injector) {
|
||||
openmct.$injector = $injector;
|
||||
|
||||
return openmct;
|
||||
},
|
||||
depends: ['$injector']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
openmct.legacyExtension = function (category, extension) {
|
||||
this.legacyBundle.extensions[category] =
|
||||
this.legacyBundle.extensions[category] || [];
|
||||
this.legacyBundle.extensions[category].push(extension);
|
||||
}.bind(openmct);
|
||||
|
||||
/**
|
||||
* Return a legacy object, for compatibility purposes only. This method
|
||||
* will be deprecated and removed in the future.
|
||||
* @private
|
||||
*/
|
||||
openmct.legacyObject = function (domainObject) {
|
||||
let capabilityService = this.$injector.get('capabilityService');
|
||||
|
||||
function instantiate(model, keyString) {
|
||||
const capabilities = capabilityService.getCapabilities(model, keyString);
|
||||
model.id = keyString;
|
||||
|
||||
return new DomainObjectImpl(keyString, model, capabilities);
|
||||
}
|
||||
|
||||
if (Array.isArray(domainObject)) {
|
||||
// an array of domain objects. [object, ...ancestors] representing
|
||||
// a single object with a given chain of ancestors. We instantiate
|
||||
// as a single contextual domain object.
|
||||
return domainObject
|
||||
.map((o) => {
|
||||
let keyString = objectUtils.makeKeyString(o.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(o);
|
||||
|
||||
return instantiate(oldModel, keyString);
|
||||
})
|
||||
.reverse()
|
||||
.reduce((parent, child) => {
|
||||
return new ContextualDomainObject(child, parent);
|
||||
});
|
||||
|
||||
} else {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
|
||||
return instantiate(oldModel, keyString);
|
||||
}
|
||||
}.bind(openmct);
|
||||
|
||||
openmct.legacyRegistry = new BundleRegistry();
|
||||
installDefaultBundles(openmct.legacyRegistry);
|
||||
|
||||
const patchedStart = openmct.start.bind(openmct);
|
||||
openmct.start = async () => {
|
||||
openmct.legacyRegistry.register('adapter', openmct.legacyBundle);
|
||||
openmct.legacyRegistry.enable('adapter');
|
||||
|
||||
openmct.legacyExtension('runs', {
|
||||
depends: ['navigationService'],
|
||||
implementation: function (navigationService) {
|
||||
navigationService
|
||||
.addListener(openmct.emit.bind(openmct, 'navigation'));
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: remove with legacy types.
|
||||
openmct.types.listKeys().forEach(function (typeKey) {
|
||||
const type = openmct.types.get(typeKey);
|
||||
const legacyDefinition = type.toLegacyDefinition();
|
||||
legacyDefinition.key = typeKey;
|
||||
openmct.legacyExtension('types', legacyDefinition);
|
||||
});
|
||||
|
||||
const main = new Main();
|
||||
const angularInstance = await main.run(openmct);
|
||||
|
||||
openmct.$angular = angularInstance;
|
||||
openmct.$injector.get('objectService');
|
||||
|
||||
return patchedStart();
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -91,11 +91,11 @@ export default class LinkAction {
|
||||
}
|
||||
|
||||
validate(currentParent) {
|
||||
return (data) => {
|
||||
const parentCandidate = data.value[0];
|
||||
return (object, data) => {
|
||||
const parentCandidate = data.value;
|
||||
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||
const objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
|
||||
|
||||
if (!parentCandidateKeystring || !currentParentKeystring) {
|
||||
return false;
|
||||
@@ -114,7 +114,7 @@ export default class LinkAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
|
||||
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +140,11 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
validate(currentParent) {
|
||||
return (data) => {
|
||||
const parentCandidatePath = data.value;
|
||||
const parentCandidate = parentCandidatePath[0];
|
||||
|
||||
return (object, data) => {
|
||||
const parentCandidate = data.value;
|
||||
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
|
||||
|
||||
if (!parentCandidateKeystring || !currentParentKeystring) {
|
||||
return false;
|
||||
@@ -165,7 +163,7 @@ export default class MoveAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
|
||||
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
|
||||
import { MY_ITEMS_KEY } from "./createMyItemsIdentifier";
|
||||
|
||||
function myItemsInterceptor(openmct, identifierObject, name) {
|
||||
function myItemsInterceptor(identifierObject, openmct) {
|
||||
|
||||
const myItemsModel = {
|
||||
identifier: identifierObject,
|
||||
name,
|
||||
type: "folder",
|
||||
composition: [],
|
||||
location: "ROOT"
|
||||
"name": "My Items",
|
||||
"type": "folder",
|
||||
"composition": [],
|
||||
"location": "ROOT"
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -23,13 +23,11 @@
|
||||
import { createMyItemsIdentifier } from "./createMyItemsIdentifier";
|
||||
import myItemsInterceptor from "./myItemsInterceptor";
|
||||
|
||||
const MY_ITEMS_DEFAULT_NAME = 'My Items';
|
||||
|
||||
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') {
|
||||
export default function MyItemsPlugin(namespace = '') {
|
||||
return function install(openmct) {
|
||||
const identifier = createMyItemsIdentifier(namespace);
|
||||
|
||||
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
|
||||
openmct.objects.addGetInterceptor(myItemsInterceptor(identifier, openmct));
|
||||
openmct.objects.addRoot(identifier);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ import {
|
||||
} from './createMyItemsIdentifier';
|
||||
|
||||
const MISSING_NAME = `Missing: ${MY_ITEMS_KEY}`;
|
||||
const DEFAULT_NAME = 'My Items';
|
||||
const FANCY_NAME = 'Fancy Items';
|
||||
const myItemsIdentifier = createMyItemsIdentifier();
|
||||
|
||||
describe("the plugin", () => {
|
||||
@@ -42,82 +40,53 @@ describe("the plugin", () => {
|
||||
name: MISSING_NAME
|
||||
};
|
||||
|
||||
describe('with no arguments passed in', () => {
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('when installed, adds "My Items" to the root', async () => {
|
||||
const root = await openmct.objects.get('ROOT');
|
||||
const rootCompostionCollection = openmct.composition.get(root);
|
||||
const rootCompostion = await rootCompostionCollection.load();
|
||||
let myItems = rootCompostion.filter((domainObject) => {
|
||||
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
|
||||
})[0];
|
||||
|
||||
expect(myItems.name).toBe(DEFAULT_NAME);
|
||||
expect(myItems).toBeDefined();
|
||||
});
|
||||
|
||||
describe('adds an interceptor that returns a "My Items" model for', () => {
|
||||
let myItemsMissing;
|
||||
let mockMissingProvider;
|
||||
let activeProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockMissingProvider = {
|
||||
get: () => Promise.resolve(missingObj),
|
||||
create: () => Promise.resolve(missingObj),
|
||||
update: () => Promise.resolve(missingObj)
|
||||
};
|
||||
|
||||
activeProvider = mockMissingProvider;
|
||||
spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
|
||||
myItemsMissing = await openmct.objects.get(myItemsIdentifier);
|
||||
});
|
||||
|
||||
it('missing objects', () => {
|
||||
let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
|
||||
|
||||
expect(myItemsMissing).toBeDefined();
|
||||
expect(idsMatchMissing).toBeTrue();
|
||||
});
|
||||
});
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
describe('with a name argument passed in', () => {
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.install(openmct.plugins.MyItems(FANCY_NAME));
|
||||
it('when installed, adds "My Items" to the root', async () => {
|
||||
const root = await openmct.objects.get('ROOT');
|
||||
const rootCompostionCollection = openmct.composition.get(root);
|
||||
const rootCompostion = await rootCompostionCollection.load();
|
||||
let myItems = rootCompostion.filter((domainObject) => {
|
||||
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
|
||||
})[0];
|
||||
|
||||
spyOn(openmct.objects, 'isMissing').and.returnValue(true);
|
||||
expect(myItems).toBeDefined();
|
||||
});
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
describe('adds an interceptor that returns a "My Items" model for', () => {
|
||||
let myItemsMissing;
|
||||
let mockMissingProvider;
|
||||
let activeProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockMissingProvider = {
|
||||
get: () => Promise.resolve(missingObj),
|
||||
create: () => Promise.resolve(missingObj),
|
||||
update: () => Promise.resolve(missingObj)
|
||||
};
|
||||
|
||||
activeProvider = mockMissingProvider;
|
||||
spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
|
||||
myItemsMissing = await openmct.objects.get(myItemsIdentifier);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
it('missing objects', () => {
|
||||
let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
|
||||
|
||||
expect(myItemsMissing).toBeDefined();
|
||||
expect(idsMatchMissing).toBeTrue();
|
||||
});
|
||||
|
||||
it('when installed, uses the passed in name', async () => {
|
||||
let myItems = await openmct.objects.get(myItemsIdentifier);
|
||||
|
||||
expect(myItems.name).toBe(FANCY_NAME);
|
||||
expect(myItems).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -142,6 +142,7 @@ import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaul
|
||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
|
||||
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
import objectLink from '../../../ui/mixins/object-link';
|
||||
@@ -454,6 +455,11 @@ export default {
|
||||
? getDefaultNotebook().defaultSectionId
|
||||
: undefined;
|
||||
},
|
||||
getDefaultNotebookObject() {
|
||||
const defaultNotebook = getDefaultNotebook();
|
||||
|
||||
return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
|
||||
},
|
||||
getLinktoNotebook() {
|
||||
const objectPath = this.openmct.router.path;
|
||||
const link = objectLink.computed.objectLink.call({
|
||||
@@ -613,12 +619,12 @@ export default {
|
||||
|
||||
this.sectionsChanged({ sections });
|
||||
},
|
||||
removeDefaultClass(defaultNotebookIdentifier) {
|
||||
if (!defaultNotebookIdentifier) {
|
||||
removeDefaultClass(domainObject) {
|
||||
if (!domainObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.openmct.status.delete(defaultNotebookIdentifier);
|
||||
this.openmct.status.delete(domainObject.identifier);
|
||||
},
|
||||
resetSearch() {
|
||||
this.search = '';
|
||||
@@ -627,16 +633,15 @@ export default {
|
||||
toggleNav() {
|
||||
this.showNav = !this.showNav;
|
||||
},
|
||||
updateDefaultNotebook(notebookStorage) {
|
||||
const defaultNotebook = getDefaultNotebook();
|
||||
const defaultNotebookIdentifier = defaultNotebook && defaultNotebook.identifier;
|
||||
const isSameNotebook = defaultNotebookIdentifier
|
||||
&& this.openmct.objects.areIdsEqual(defaultNotebookIdentifier, notebookStorage.identifier);
|
||||
async updateDefaultNotebook(notebookStorage) {
|
||||
const defaultNotebookObject = await this.getDefaultNotebookObject();
|
||||
const isSameNotebook = defaultNotebookObject
|
||||
&& objectUtils.makeKeyString(defaultNotebookObject.identifier) === objectUtils.makeKeyString(notebookStorage.identifier);
|
||||
if (!isSameNotebook) {
|
||||
this.removeDefaultClass(defaultNotebookIdentifier);
|
||||
this.removeDefaultClass(defaultNotebookObject);
|
||||
}
|
||||
|
||||
if (!defaultNotebookIdentifier || !isSameNotebook) {
|
||||
if (!defaultNotebookObject || !isSameNotebook) {
|
||||
setDefaultNotebook(this.openmct, notebookStorage, this.domainObject);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,8 +110,7 @@ export default class Snapshot {
|
||||
}
|
||||
|
||||
return () => {
|
||||
const path = window.location.href.split('#');
|
||||
window.location.href = path[0] + url;
|
||||
window.location.href = window.location.origin + url;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,11 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
|
||||
const date = Date.now();
|
||||
const configuration = domainObject.configuration;
|
||||
const entries = configuration.entries || {};
|
||||
|
||||
if (!entries) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embeds = embed
|
||||
? [embed]
|
||||
: [];
|
||||
@@ -120,8 +125,7 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
|
||||
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
|
||||
|
||||
addDefaultClass(domainObject, openmct);
|
||||
|
||||
mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
|
||||
domainObject.configuration.entries = newEntries;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
describe('the plugin', () => {
|
||||
let notificationIndicatorPlugin;
|
||||
let openmct;
|
||||
let indicatorObject;
|
||||
let indicatorElement;
|
||||
let parentElement;
|
||||
let mockMessages = ['error', 'test', 'notifications'];
|
||||
@@ -42,6 +43,9 @@ describe('the plugin', () => {
|
||||
|
||||
parentElement = document.createElement('div');
|
||||
|
||||
indicatorObject = openmct.indicators.indicatorObjects.find(indicator => indicator.key === 'notifications-indicator');
|
||||
indicatorElement = indicatorObject.element;
|
||||
|
||||
openmct.on('start', () => {
|
||||
mockMessages.forEach(message => {
|
||||
openmct.notifications.error(message);
|
||||
@@ -49,7 +53,7 @@ describe('the plugin', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
openmct.start();
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -64,7 +68,7 @@ describe('the plugin', () => {
|
||||
});
|
||||
|
||||
it('notifies the user of the number of notifications', () => {
|
||||
let notificationCountElement = document.querySelector('.c-indicator__count');
|
||||
let notificationCountElement = parentElement.querySelector('.c-indicator__count');
|
||||
|
||||
expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
|
||||
});
|
||||
|
||||
@@ -278,7 +278,7 @@ export default {
|
||||
// Have to throw away the old canvas elements and replace with new
|
||||
// canvas elements in order to get new drawing contexts.
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
|
||||
div.innerHTML = this.TEMPLATE;
|
||||
const mainCanvas = div.querySelectorAll("canvas")[1];
|
||||
const overlayCanvas = div.querySelectorAll("canvas")[0];
|
||||
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);
|
||||
|
||||
@@ -533,6 +533,13 @@ describe("the plugin", function () {
|
||||
let plotViewComponentObject;
|
||||
|
||||
beforeEach(() => {
|
||||
const getFunc = openmct.$injector.get;
|
||||
spyOn(openmct.$injector, "get")
|
||||
.withArgs("exportImageService").and.returnValue({
|
||||
exportPNG: () => {},
|
||||
exportJPG: () => {}
|
||||
})
|
||||
.and.callFake(getFunc);
|
||||
|
||||
stackedPlotObject = {
|
||||
identifier: {
|
||||
|
||||
@@ -74,9 +74,7 @@ define([
|
||||
'./clock/plugin',
|
||||
'./DeviceClassifier/plugin',
|
||||
'./timer/plugin',
|
||||
'./localStorage/plugin',
|
||||
'./legacySupport/plugin.js',
|
||||
'../adapter/indicators/legacy-indicators-plugin'
|
||||
'./localStorage/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
@@ -131,9 +129,7 @@ define([
|
||||
Clock,
|
||||
DeviceClassifier,
|
||||
Timer,
|
||||
LocalStorage,
|
||||
LegacySupportPlugin,
|
||||
LegacyIndicatorsPlugin
|
||||
LocalStorage
|
||||
) {
|
||||
const bundleMap = {
|
||||
Elasticsearch: 'platform/persistence/elastic'
|
||||
@@ -241,8 +237,6 @@ define([
|
||||
plugins.Timer = Timer.default;
|
||||
plugins.DeviceClassifier = DeviceClassifier.default;
|
||||
plugins.LocalStorage = LocalStorage.default;
|
||||
plugins.LegacySupport = LegacySupportPlugin.default;
|
||||
plugins.LegacyIndicators = LegacyIndicatorsPlugin;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
@@ -29,9 +29,10 @@ define(
|
||||
}
|
||||
|
||||
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
|
||||
const parentType = parent.type;
|
||||
const parentType = parent.getCapability('type');
|
||||
const newStyleChild = child.useCapability('adapter');
|
||||
|
||||
if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) {
|
||||
if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ define([
|
||||
const widgetType = {
|
||||
name: 'Summary Widget',
|
||||
description: 'A compact status update for collections of telemetry-producing items',
|
||||
creatable: true,
|
||||
cssClass: 'icon-summary-widget',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.composition = [];
|
||||
@@ -84,8 +85,16 @@ define([
|
||||
|
||||
return function install(openmct) {
|
||||
openmct.types.addType('summary-widget', widgetType);
|
||||
let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct);
|
||||
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
|
||||
openmct.legacyExtension('policies', {
|
||||
category: 'composition',
|
||||
implementation: SummaryWidgetsCompositionPolicy,
|
||||
depends: ['openmct']
|
||||
});
|
||||
openmct.legacyExtension('policies', {
|
||||
category: 'view',
|
||||
implementation: SummaryWidgetViewPolicy,
|
||||
depends: ['openmct']
|
||||
});
|
||||
openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct));
|
||||
openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
|
||||
openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));
|
||||
|
||||
@@ -80,6 +80,7 @@ define([
|
||||
this.addHyperlink(domainObject.url, domainObject.openNewTab);
|
||||
this.watchForChanges(openmct, domainObject);
|
||||
|
||||
const id = objectUtils.makeKeyString(this.domainObject.identifier);
|
||||
const self = this;
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,8 @@ define([
|
||||
|
||||
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
|
||||
|
||||
openmct.$injector.get('objectService')
|
||||
.getObjects([id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -191,7 +191,7 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
destroyed() {
|
||||
this.active = false;
|
||||
if (this.unlisten) {
|
||||
this.unlisten();
|
||||
|
||||
@@ -60,8 +60,6 @@ describe("Timer plugin:", () => {
|
||||
timerDefinition = openmct.types.get('timer').definition;
|
||||
timerDefinition.initialize(timerDomainObject);
|
||||
|
||||
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
|
||||
|
||||
openmct.on('start', resolve);
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
@@ -95,8 +93,6 @@ describe("Timer plugin:", () => {
|
||||
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
|
||||
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
|
||||
|
||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
|
||||
|
||||
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
|
||||
|
||||
timerObjectPath = [mutableTimerObject];
|
||||
@@ -106,10 +102,6 @@ describe("Timer plugin:", () => {
|
||||
await Vue.nextTick();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
timerView.destroy();
|
||||
});
|
||||
|
||||
it("should migrate old object properties to the configuration section", () => {
|
||||
openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
|
||||
expect(timerViewObject.configuration.timerFormat).toBe('short');
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
export const COLOR_PALETTE = [
|
||||
[0x43, 0xB0, 0xFF],
|
||||
[0x00, 0x37, 0xFF],
|
||||
[0xF0, 0x60, 0x00],
|
||||
[0x00, 0x70, 0x40],
|
||||
[0xFB, 0x49, 0x49],
|
||||
@@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
|
||||
[0xFF, 0xA6, 0x3D],
|
||||
[0x05, 0xA3, 0x00],
|
||||
[0xF0, 0x00, 0x6C],
|
||||
[0xAC, 0x54, 0xAE],
|
||||
[0x77, 0x17, 0x7A],
|
||||
[0x23, 0xA9, 0xDB],
|
||||
[0xC7, 0xBE, 0x52],
|
||||
[0x5A, 0xBD, 0x56],
|
||||
[0xFA, 0xF0, 0x6F],
|
||||
[0x4E, 0xF0, 0x48],
|
||||
[0xAD, 0x50, 0x72],
|
||||
[0x94, 0x25, 0xEA],
|
||||
[0x21, 0x87, 0x82],
|
||||
[0x8F, 0x6E, 0x47],
|
||||
[0xf0, 0x59, 0xcb],
|
||||
[0x34, 0xB6, 0x7D],
|
||||
[0x7F, 0x52, 0xFF],
|
||||
[0x46, 0xC7, 0xC0],
|
||||
[0x6A, 0x36, 0xFF],
|
||||
[0x56, 0xF0, 0xE8],
|
||||
[0xA1, 0x8C, 0x1C],
|
||||
[0x95, 0xB1, 0x26],
|
||||
[0xCB, 0xE1, 0x44],
|
||||
[0xFF, 0x84, 0x9E],
|
||||
[0xB7, 0x79, 0xE7],
|
||||
[0x8C, 0xC9, 0xFD],
|
||||
[0xDB, 0xAA, 0x6E],
|
||||
[0x93, 0xB5, 0x77],
|
||||
[0xB8, 0xDF, 0x97],
|
||||
[0xFF, 0xBC, 0xDA],
|
||||
[0xD3, 0xB6, 0xDE]
|
||||
];
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div ref="objectViewWrapper"
|
||||
class="c-object-view"
|
||||
:class="objectViewStyle"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -64,6 +64,13 @@ export default {
|
||||
},
|
||||
font() {
|
||||
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
|
||||
},
|
||||
objectViewStyle() {
|
||||
if (this.domainObject && this.domainObject.type === 'time-strip') {
|
||||
return 'l-shell__main-object-view';
|
||||
} else {
|
||||
return 'u-contents';
|
||||
}
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
@@ -141,15 +148,11 @@ export default {
|
||||
this.openmct.objectViews.off('clearData', this.clearData);
|
||||
},
|
||||
getStyleReceiver() {
|
||||
let styleReceiver;
|
||||
let styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|
||||
|| this.$refs.objectViewWrapper.querySelector(':first-child');
|
||||
|
||||
if (this.$refs.objectViewWrapper !== undefined) {
|
||||
styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|
||||
|| this.$refs.objectViewWrapper.querySelector(':first-child');
|
||||
|
||||
if (styleReceiver === null) {
|
||||
styleReceiver = undefined;
|
||||
}
|
||||
if (styleReceiver === null) {
|
||||
styleReceiver = undefined;
|
||||
}
|
||||
|
||||
return styleReceiver;
|
||||
|
||||
@@ -186,10 +186,6 @@ export default {
|
||||
return {
|
||||
name: field.name,
|
||||
value: field.path.reduce((object, key) => {
|
||||
if (object === undefined) {
|
||||
return object;
|
||||
}
|
||||
|
||||
return object[key];
|
||||
}, this.domainObject)
|
||||
};
|
||||
|
||||
@@ -233,6 +233,7 @@
|
||||
/******************************* MAIN AREA */
|
||||
&__main-container {
|
||||
// Wrapper for main views
|
||||
//display: flex; NEEDS REGRESSION TESTING!!!
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto !important;
|
||||
@@ -242,11 +243,11 @@
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
> .c-object-view {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
&__main-object-view {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&__tree {
|
||||
@@ -316,12 +317,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.c-object-view {
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.is-editing {
|
||||
.l-shell__main-container {
|
||||
$m: 3px;
|
||||
|
||||
@@ -445,10 +445,6 @@ export default {
|
||||
}
|
||||
|
||||
// sorting composition items
|
||||
if (!a.name || !b.name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a.name.toLowerCase()
|
||||
> b.name.toLowerCase()) {
|
||||
return 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import objectPathToUrl from '../../tools/url';
|
||||
import objectPathToUrl from '/src/tools/url';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
|
||||
@@ -24,6 +24,8 @@ class Ticker {
|
||||
constructor() {
|
||||
this.callbacks = [];
|
||||
this.last = new Date() - 1000;
|
||||
|
||||
this.tick();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,7 @@ class Ticker {
|
||||
}
|
||||
|
||||
// Try to update at exactly the next second
|
||||
this.timeoutHandle = setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
this.tick();
|
||||
}, 1000 - millis, true);
|
||||
}
|
||||
@@ -60,10 +62,6 @@ class Ticker {
|
||||
* @returns {Function} a function to unregister this listener
|
||||
*/
|
||||
listen(callback) {
|
||||
if (this.callbacks.length === 0) {
|
||||
this.tick();
|
||||
}
|
||||
|
||||
this.callbacks.push(callback);
|
||||
|
||||
// Provide immediate feedback
|
||||
@@ -74,10 +72,6 @@ class Ticker {
|
||||
this.callbacks = this.callbacks.filter(function (cb) {
|
||||
return cb !== callback;
|
||||
});
|
||||
|
||||
if (this.callbacks.length === 0) {
|
||||
clearTimeout(this.timeoutHandle);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,5 +127,5 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
},
|
||||
stats: 'errors-warnings'
|
||||
stats: 'detailed'
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user