mirror of
https://github.com/nasa/openmct.git
synced 2025-06-30 12:43:04 +00:00
Compare commits
35 Commits
code-cover
...
test-multi
Author | SHA1 | Date | |
---|---|---|---|
555497604c | |||
e581ccb120 | |||
6bcc9bfd84 | |||
f42f291790 | |||
176e8167f0 | |||
87d58904b4 | |||
ae629a6c8b | |||
3f575f0ec0 | |||
b3ab56cb57 | |||
f6934a43c9 | |||
22a7537974 | |||
3620760991 | |||
88a94c80be | |||
2fc0d34b8f | |||
d53ca3ec9a | |||
86e5d10fc1 | |||
936b88363c | |||
38fec73a33 | |||
43c2c8543e | |||
e8e719e7f7 | |||
26e70d82b7 | |||
3a65f75d21 | |||
51e4c0c836 | |||
bb9c225f23 | |||
19ec98af79 | |||
23ead2ceaa | |||
6a8f4b5d9c | |||
464bb3b885 | |||
4775c88909 | |||
722e2e2bb1 | |||
333aa1d6db | |||
5e92c69fe2 | |||
8ddba2b06f | |||
6f9241c0b1 | |||
d84808aa68 |
@ -2,7 +2,7 @@ version: 2.1
|
|||||||
executors:
|
executors:
|
||||||
pw-focal-development:
|
pw-focal-development:
|
||||||
docker:
|
docker:
|
||||||
- image: mcr.microsoft.com/playwright:focal
|
- image: mcr.microsoft.com/playwright:v1.17.2-focal
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||||
parameters:
|
parameters:
|
||||||
@ -22,7 +22,7 @@ commands:
|
|||||||
node-version: << parameters.node-version >>
|
node-version: << parameters.node-version >>
|
||||||
- node/install:
|
- node/install:
|
||||||
install-npm: true
|
install-npm: true
|
||||||
node-version: lts/fermium
|
node-version: << parameters.node-version >>
|
||||||
- run: npm install
|
- run: npm install
|
||||||
restore_cache_cmd:
|
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"
|
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
|
||||||
@ -76,14 +76,14 @@ jobs:
|
|||||||
node-version: <<parameters.node-version>>
|
node-version: <<parameters.node-version>>
|
||||||
- run: npm audit --audit-level=low
|
- run: npm audit --audit-level=low
|
||||||
- generate_and_store_version_and_filesystem_artifacts
|
- generate_and_store_version_and_filesystem_artifacts
|
||||||
node10-lint:
|
node14-lint:
|
||||||
|
parameters:
|
||||||
|
node-version:
|
||||||
|
type: string
|
||||||
executor: pw-focal-development
|
executor: pw-focal-development
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- build_and_install:
|
||||||
- node/install:
|
node-version: <<parameters.node-version>>
|
||||||
install-npm: false #Cannot install latest npm version with node10.
|
|
||||||
node-version: lts/dubnium
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run lint
|
- run: npm run lint
|
||||||
- generate_and_store_version_and_filesystem_artifacts
|
- generate_and_store_version_and_filesystem_artifacts
|
||||||
unit-test:
|
unit-test:
|
||||||
@ -141,7 +141,8 @@ jobs:
|
|||||||
workflows:
|
workflows:
|
||||||
overall-circleci-commit-status: #These jobs run on every commit
|
overall-circleci-commit-status: #These jobs run on every commit
|
||||||
jobs:
|
jobs:
|
||||||
- node10-lint
|
- node14-lint:
|
||||||
|
node-version: lts/fermium
|
||||||
- unit-test:
|
- unit-test:
|
||||||
name: node12-chrome
|
name: node12-chrome
|
||||||
node-version: lts/erbium
|
node-version: lts/erbium
|
||||||
@ -153,15 +154,11 @@ workflows:
|
|||||||
post-steps:
|
post-steps:
|
||||||
- upload_code_covio
|
- upload_code_covio
|
||||||
- e2e-test:
|
- e2e-test:
|
||||||
name: e2e-smoke
|
name: e2e-ci
|
||||||
node-version: lts/fermium
|
node-version: lts/fermium
|
||||||
suite: ci
|
suite: ci
|
||||||
the-nightly: #These jobs do not run on PRs, but against master at night
|
the-nightly: #These jobs do not run on PRs, but against master at night
|
||||||
jobs:
|
jobs:
|
||||||
- unit-test:
|
|
||||||
name: node10-chrome-nightly
|
|
||||||
node-version: lts/dubnium
|
|
||||||
browser: ChromeHeadless
|
|
||||||
- unit-test:
|
- unit-test:
|
||||||
name: node12-firefoxESR-nightly
|
name: node12-firefoxESR-nightly
|
||||||
node-version: lts/erbium
|
node-version: lts/erbium
|
||||||
|
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?
|
- [ ] Regression? Did this used to work or has it always been broken?
|
||||||
- [ ] Is there a workaround available?
|
- [ ] Is there a workaround available?
|
||||||
- [ ] Does this impact a critical component?
|
- [ ] Does this impact a critical component?
|
||||||
- [ ] Is this just a visual bug?
|
- [ ] Is this just a visual bug with no functional impact?
|
||||||
|
|
||||||
#### Steps to Reproduce
|
#### Steps to Reproduce
|
||||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||||
|
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@ -11,11 +11,12 @@ updates:
|
|||||||
- "dependencies"
|
- "dependencies"
|
||||||
- "pr:e2e"
|
- "pr:e2e"
|
||||||
allow:
|
allow:
|
||||||
- dependency-name: "eslint*"
|
- dependency-name: "*eslint*"
|
||||||
- dependency-name: "karma*"
|
- dependency-name: "*karma*"
|
||||||
- dependency-name: "jasmine*"
|
- dependency-name: "*jasmine*"
|
||||||
- dependency-name: "playwright*"
|
- dependency-name: "*playwright*"
|
||||||
- dependency-name: "percy*"
|
- dependency-name: "*percy*"
|
||||||
|
- dependency-name: "*vue-loader*"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
5
.npmrc
5
.npmrc
@ -1 +1,6 @@
|
|||||||
loglevel=warn
|
loglevel=warn
|
||||||
|
|
||||||
|
# Temporary: istanbul-instrumenter-loader is working with webpack 5, but states
|
||||||
|
# webpack 4 being the latest version it supports, so this legacy-peer-deps
|
||||||
|
# allows us to install it anyway.
|
||||||
|
legacy-peer-deps=true
|
42
API.md
42
API.md
@ -52,6 +52,8 @@
|
|||||||
- [The URL Status Indicator](#the-url-status-indicator)
|
- [The URL Status Indicator](#the-url-status-indicator)
|
||||||
- [Creating a Simple Indicator](#creating-a-simple-indicator)
|
- [Creating a Simple Indicator](#creating-a-simple-indicator)
|
||||||
- [Custom Indicators](#custom-indicators)
|
- [Custom Indicators](#custom-indicators)
|
||||||
|
- [Priority API](#priority-api)
|
||||||
|
- [Priority Types](#priority-types)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
@ -249,14 +251,22 @@ eg.
|
|||||||
openmct.objects.addRoot({
|
openmct.objects.addRoot({
|
||||||
namespace: "example.namespace",
|
namespace: "example.namespace",
|
||||||
key: "my-key"
|
key: "my-key"
|
||||||
});
|
},
|
||||||
|
openmct.priority.HIGH);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
|
The `addRoot` function takes a two arguments, the first can be an [object identifier](#domain-objects-and-identifiers) for a root level object, or an array of identifiers for root
|
||||||
as an argument.
|
level objects, or a function that returns a promise for an identifier or an array of root level objects, the second is a [priority](#priority-api) or numeric value.
|
||||||
|
|
||||||
Root objects are loaded just like any other objects, i.e. via an object
|
When using the `getAll` method of the object API, they will be returned in order of priority.
|
||||||
provider.
|
|
||||||
|
eg.
|
||||||
|
```javascript
|
||||||
|
openmct.objects.addRoot(identifier, openmct.priority.LOW); // low = -1000, will appear last in composition or tree
|
||||||
|
openmct.objects.addRoot(otherIdentifier, openmct.priority.HIGH); // high = 1000, will appear first in composition or tree
|
||||||
|
```
|
||||||
|
|
||||||
|
Root objects are loaded just like any other objects, i.e. via an object provider.
|
||||||
|
|
||||||
## Object Providers
|
## Object Providers
|
||||||
|
|
||||||
@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to
|
|||||||
element: domNode
|
element: domNode
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Priority API
|
||||||
|
|
||||||
|
Open MCT provides some built-in priority values that can be used in the application for view providers, indicators, root object order, and more.
|
||||||
|
|
||||||
|
### Priority Types
|
||||||
|
|
||||||
|
Currently, the Open MCT Priority API provides (type: numeric value):
|
||||||
|
- HIGH: 1000
|
||||||
|
- Default: 0
|
||||||
|
- LOW: -1000
|
||||||
|
|
||||||
|
View provider Example:
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
class ViewProvider {
|
||||||
|
...
|
||||||
|
priority() {
|
||||||
|
return openmct.priority.HIGH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -1,4 +1,4 @@
|
|||||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct) [](https://percy.io/b2e34b17/openmct)
|
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct) [](https://percy.io/b2e34b17/openmct) [](https://www.npmjs.com/package/openmct)
|
||||||
|
|
||||||
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
||||||
|
|
||||||
|
20
app.js
20
app.js
@ -7,7 +7,6 @@
|
|||||||
* node app.js [options]
|
* node app.js [options]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
const options = require('minimist')(process.argv.slice(2));
|
const options = require('minimist')(process.argv.slice(2));
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const app = express();
|
const app = express();
|
||||||
@ -40,10 +39,19 @@ app.use('/proxyUrl', function proxyRequest(req, res, next) {
|
|||||||
}).on('error', next)).pipe(res);
|
}).on('error', next)).pipe(res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
class WatchRunPlugin {
|
||||||
|
apply(compiler) {
|
||||||
|
compiler.hooks.emit.tapAsync('WatchRunPlugin', (compilation, callback) => {
|
||||||
|
console.log('Begin compile at ' + new Date());
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const webpackConfig = require('./webpack.config.js');
|
const webpackConfig = require('./webpack.dev.js');
|
||||||
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
|
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||||
webpackConfig.plugins.push(function() { this.plugin('watch-run', function(watching, callback) { console.log('Begin compile at ' + new Date()); callback(); }) });
|
webpackConfig.plugins.push(new WatchRunPlugin());
|
||||||
|
|
||||||
webpackConfig.entry.openmct = [
|
webpackConfig.entry.openmct = [
|
||||||
'webpack-hot-middleware/client?reload=true',
|
'webpack-hot-middleware/client?reload=true',
|
||||||
@ -62,9 +70,7 @@ app.use(require('webpack-dev-middleware')(
|
|||||||
|
|
||||||
app.use(require('webpack-hot-middleware')(
|
app.use(require('webpack-hot-middleware')(
|
||||||
compiler,
|
compiler,
|
||||||
{
|
{}
|
||||||
|
|
||||||
}
|
|
||||||
));
|
));
|
||||||
|
|
||||||
// Expose index.html for development users.
|
// Expose index.html for development users.
|
||||||
@ -74,5 +80,5 @@ app.get('/', function (req, res) {
|
|||||||
|
|
||||||
// Finally, open the HTTP server and log the instance to the console
|
// Finally, open the HTTP server and log the instance to the console
|
||||||
app.listen(options.port, options.host, function() {
|
app.listen(options.port, options.host, function() {
|
||||||
console.log('Open MCT application running at %s:%s', options.host, options.port)
|
console.log('Open MCT application running at %s:%s', options.host, options.port);
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ const config = {
|
|||||||
timeout: 200 * 1000,
|
timeout: 200 * 1000,
|
||||||
reuseExistingServer: !process.env.CI
|
reuseExistingServer: !process.env.CI
|
||||||
},
|
},
|
||||||
|
workers: 2, //Limit to 2 for CircleCI Agent
|
||||||
use: {
|
use: {
|
||||||
browserName: "chromium",
|
browserName: "chromium",
|
||||||
baseURL: 'http://localhost:8080/',
|
baseURL: 'http://localhost:8080/',
|
||||||
|
35
index.html
35
index.html
@ -75,13 +75,36 @@
|
|||||||
const TWO_HOURS = ONE_HOUR * 2;
|
const TWO_HOURS = ONE_HOUR * 2;
|
||||||
const ONE_DAY = ONE_HOUR * 24;
|
const ONE_DAY = ONE_HOUR * 24;
|
||||||
|
|
||||||
[
|
|
||||||
'example/eventGenerator'
|
|
||||||
].forEach(
|
|
||||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
|
||||||
);
|
|
||||||
|
|
||||||
openmct.install(openmct.plugins.LocalStorage());
|
openmct.install(openmct.plugins.LocalStorage());
|
||||||
|
openmct.install(openmct.plugins.LocalStorage('namespace1', 'namespace1'));
|
||||||
|
openmct.install(openmct.plugins.LocalStorage('namespace2', 'namespace2'));
|
||||||
|
openmct.install((openmct) => {
|
||||||
|
openmct.objects.addRoot({
|
||||||
|
namespace: 'namespace1',
|
||||||
|
key: 'root'
|
||||||
|
});
|
||||||
|
openmct.objects.addRoot({
|
||||||
|
namespace: 'namespace2',
|
||||||
|
key: 'root'
|
||||||
|
});
|
||||||
|
openmct.objects.addGetInterceptor({
|
||||||
|
appliesTo: (identifier, object) => {
|
||||||
|
return (identifier.namespace === 'namespace1'
|
||||||
|
|| identifier.namespace === 'namespace2')
|
||||||
|
&& identifier.key === 'root'
|
||||||
|
&& (object === undefined || object.type === 'missing');
|
||||||
|
},
|
||||||
|
invoke: (identifier, domainObject) => {
|
||||||
|
return {
|
||||||
|
identifier: identifier,
|
||||||
|
type: 'folder',
|
||||||
|
location: 'mine',
|
||||||
|
name: identifier.namespace,
|
||||||
|
composition: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Espresso());
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
/*global module,process*/
|
/*global module,process*/
|
||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
|
||||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
||||||
const coverageEnabled = process.env.COVERAGE === 'true';
|
const coverageEnabled = process.env.COVERAGE === 'true';
|
||||||
const reporters = ['spec', 'junit'];
|
const reporters = ['spec', 'junit'];
|
||||||
@ -32,10 +31,10 @@ if (coverageEnabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (config) => {
|
module.exports = (config) => {
|
||||||
const webpackConfig = require('./webpack.config.js');
|
const webpackConfig = require('./webpack.dev.js');
|
||||||
delete webpackConfig.output;
|
delete webpackConfig.output;
|
||||||
|
|
||||||
if (!devMode || coverageEnabled) {
|
if (coverageEnabled) {
|
||||||
webpackConfig.module.rules.push({
|
webpackConfig.module.rules.push({
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules|example|lib|dist/,
|
exclude: /node_modules|example|lib|dist/,
|
||||||
|
52
package.json
52
package.json
@ -1,33 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "1.8.2",
|
"version": "1.8.3-SNAPSHOT",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@braintree/sanitize-url": "^5.0.2",
|
"@braintree/sanitize-url": "^5.0.2",
|
||||||
"@percy/cli": "^1.0.0-beta.70",
|
"@percy/cli": "^1.0.0-beta.71",
|
||||||
"@percy/playwright": "^1.0.1",
|
"@percy/playwright": "^1.0.1",
|
||||||
"@playwright/test": "^1.16.3",
|
"@playwright/test": "^1.17.2",
|
||||||
"allure-playwright": "^2.0.0-beta.14",
|
"allure-playwright": "^2.0.0-beta.14",
|
||||||
"angular": ">=1.8.0",
|
"angular": ">=1.8.0",
|
||||||
"angular-route": "1.4.14",
|
"angular-route": "1.4.14",
|
||||||
"babel-eslint": "10.0.3",
|
"babel-eslint": "10.1.0",
|
||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
"concurrently": "^3.6.1",
|
"concurrently": "^3.6.1",
|
||||||
"copy-webpack-plugin": "^4.5.2",
|
"copy-webpack-plugin": "^10.2.0",
|
||||||
"cross-env": "^6.0.3",
|
"cross-env": "^6.0.3",
|
||||||
"css-loader": "^1.0.0",
|
"css-loader": "^4.0.0",
|
||||||
"d3-axis": "1.0.x",
|
"d3-axis": "1.0.x",
|
||||||
"d3-scale": "1.0.x",
|
"d3-scale": "1.0.x",
|
||||||
"d3-selection": "1.3.x",
|
"d3-selection": "1.3.x",
|
||||||
"eslint": "7.0.0",
|
"eslint": "7.0.0",
|
||||||
"eslint-plugin-playwright": "0.7.0",
|
"eslint-plugin-playwright": "0.7.1",
|
||||||
"eslint-plugin-vue": "^7.5.0",
|
"eslint-plugin-vue": "^7.5.0",
|
||||||
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
|
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
|
||||||
"eventemitter3": "^1.2.0",
|
"eventemitter3": "^1.2.0",
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
"fast-sass-loader": "1.4.6",
|
"file-loader": "^6.1.0",
|
||||||
"file-loader": "^1.1.11",
|
|
||||||
"file-saver": "^1.3.8",
|
"file-saver": "^1.3.8",
|
||||||
"git-rev-sync": "^1.4.0",
|
"git-rev-sync": "^1.4.0",
|
||||||
"glob": ">= 3.0.0",
|
"glob": ">= 3.0.0",
|
||||||
@ -35,9 +34,9 @@
|
|||||||
"html2canvas": "^1.0.0-rc.7",
|
"html2canvas": "^1.0.0-rc.7",
|
||||||
"imports-loader": "^0.8.0",
|
"imports-loader": "^0.8.0",
|
||||||
"istanbul-instrumenter-loader": "^3.0.1",
|
"istanbul-instrumenter-loader": "^3.0.1",
|
||||||
"jasmine-core": "^3.7.1",
|
"jasmine-core": "^4.0.0",
|
||||||
"jsdoc": "^3.3.2",
|
"jsdoc": "^3.3.2",
|
||||||
"karma": "6.3.9",
|
"karma": "6.3.10",
|
||||||
"karma-chrome-launcher": "3.1.0",
|
"karma-chrome-launcher": "3.1.0",
|
||||||
"karma-cli": "2.0.0",
|
"karma-cli": "2.0.0",
|
||||||
"karma-coverage": "2.1.0",
|
"karma-coverage": "2.1.0",
|
||||||
@ -46,39 +45,42 @@
|
|||||||
"karma-jasmine": "4.0.1",
|
"karma-jasmine": "4.0.1",
|
||||||
"karma-junit-reporter": "2.0.1",
|
"karma-junit-reporter": "2.0.1",
|
||||||
"karma-sourcemap-loader": "0.3.8",
|
"karma-sourcemap-loader": "0.3.8",
|
||||||
"karma-spec-reporter": "0.0.32",
|
"karma-spec-reporter": "0.0.33",
|
||||||
"karma-webpack": "4.0.2",
|
"karma-webpack": "^5.0.0",
|
||||||
"location-bar": "^3.0.1",
|
"location-bar": "^3.0.1",
|
||||||
"lodash": "^4.17.12",
|
"lodash": "^4.17.12",
|
||||||
"markdown-toc": "^0.11.7",
|
"markdown-toc": "^0.11.7",
|
||||||
"marked": "^0.3.5",
|
"marked": "^0.3.5",
|
||||||
"mini-css-extract-plugin": "^0.4.1",
|
"mini-css-extract-plugin": "2.4.5",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"moment": "2.25.3",
|
"moment": "2.25.3",
|
||||||
"moment-duration-format": "^2.2.2",
|
"moment-duration-format": "^2.2.2",
|
||||||
"moment-timezone": "0.5.28",
|
"moment-timezone": "0.5.28",
|
||||||
"node-bourbon": "^4.2.3",
|
"node-bourbon": "^4.2.3",
|
||||||
"node-sass": "^4.14.1",
|
|
||||||
"painterro": "^1.2.56",
|
"painterro": "^1.2.56",
|
||||||
"playwright": "^1.16.3",
|
"playwright": "^1.17.2",
|
||||||
"plotly.js-basic-dist": "^2.5.0",
|
"plotly.js-basic-dist": "^2.5.0",
|
||||||
"plotly.js-gl2d-dist": "^2.5.0",
|
"plotly.js-gl2d-dist": "^2.5.0",
|
||||||
"printj": "^1.2.1",
|
"printj": "^1.2.1",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
|
"resolve-url-loader": "^4.0.0",
|
||||||
|
"sass": "^1.42.1",
|
||||||
|
"sass-loader": "^12.1.0",
|
||||||
"sinon": "^12.0.1",
|
"sinon": "^12.0.1",
|
||||||
"split": "^1.0.0",
|
"split": "^1.0.0",
|
||||||
"style-loader": "^1.0.1",
|
"style-loader": "^1.0.1",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^3.3.3",
|
||||||
"v8-compile-cache": "^1.1.0",
|
"v8-compile-cache": "^1.1.0",
|
||||||
"vue": "2.5.6",
|
"vue": "2.5.6",
|
||||||
"vue-eslint-parser": "7.11.0",
|
"vue-eslint-parser": "8.0.1",
|
||||||
"vue-loader": "^15.2.6",
|
"vue-loader": "15.9.8",
|
||||||
"vue-template-compiler": "2.5.6",
|
"vue-template-compiler": "2.5.6",
|
||||||
"webpack": "^4.16.2",
|
"webpack": "^5.65.0",
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^4.0.0",
|
||||||
"webpack-dev-middleware": "^3.1.3",
|
"webpack-dev-middleware": "^3.1.3",
|
||||||
"webpack-hot-middleware": "^2.22.3",
|
"webpack-hot-middleware": "^2.22.3",
|
||||||
|
"webpack-merge": "^5.8.0",
|
||||||
"zepto": "^1.2.0"
|
"zepto": "^1.2.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -87,14 +89,14 @@
|
|||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||||
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||||
"build:prod": "cross-env NODE_ENV=production webpack",
|
"build:prod": "cross-env webpack --config webpack.prod.js",
|
||||||
"build:dev": "webpack",
|
"build:dev": "webpack --config webpack.dev.js",
|
||||||
"build:watch": "webpack --watch",
|
"build:watch": "webpack --config webpack.dev.js --watch",
|
||||||
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
||||||
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||||
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
|
"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: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:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke default condition.e2e",
|
||||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
|
"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 default",
|
||||||
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
||||||
@ -110,7 +112,7 @@
|
|||||||
"url": "https://github.com/nasa/openmct.git"
|
"url": "https://github.com/nasa/openmct.git"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.12.2 <16.0.0"
|
"node": ">=12.0.1 <15.0.0"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
109
src/MCT.js
109
src/MCT.js
@ -22,25 +22,17 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'EventEmitter',
|
'EventEmitter',
|
||||||
'uuid',
|
|
||||||
'./BundleRegistry',
|
|
||||||
'./installDefaultBundles',
|
|
||||||
'./api/api',
|
'./api/api',
|
||||||
'./api/overlays/OverlayAPI',
|
'./api/overlays/OverlayAPI',
|
||||||
'./selection/Selection',
|
'./selection/Selection',
|
||||||
'objectUtils',
|
|
||||||
'./plugins/plugins',
|
'./plugins/plugins',
|
||||||
'./adapter/indicators/legacy-indicators-plugin',
|
|
||||||
'./ui/registries/ViewRegistry',
|
'./ui/registries/ViewRegistry',
|
||||||
'./plugins/imagery/plugin',
|
'./plugins/imagery/plugin',
|
||||||
'./ui/registries/InspectorViewRegistry',
|
'./ui/registries/InspectorViewRegistry',
|
||||||
'./ui/registries/ToolbarRegistry',
|
'./ui/registries/ToolbarRegistry',
|
||||||
'./ui/router/ApplicationRouter',
|
'./ui/router/ApplicationRouter',
|
||||||
'./ui/router/Browse',
|
'./ui/router/Browse',
|
||||||
'../platform/framework/src/Main',
|
|
||||||
'./ui/layout/Layout.vue',
|
'./ui/layout/Layout.vue',
|
||||||
'../platform/core/src/objects/DomainObjectImpl',
|
|
||||||
'../platform/core/src/capabilities/ContextualDomainObject',
|
|
||||||
'./ui/preview/plugin',
|
'./ui/preview/plugin',
|
||||||
'./api/Branding',
|
'./api/Branding',
|
||||||
'./plugins/licenses/plugin',
|
'./plugins/licenses/plugin',
|
||||||
@ -53,25 +45,17 @@ define([
|
|||||||
'vue'
|
'vue'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
uuid,
|
|
||||||
BundleRegistry,
|
|
||||||
installDefaultBundles,
|
|
||||||
api,
|
api,
|
||||||
OverlayAPI,
|
OverlayAPI,
|
||||||
Selection,
|
Selection,
|
||||||
objectUtils,
|
|
||||||
plugins,
|
plugins,
|
||||||
LegacyIndicatorsPlugin,
|
|
||||||
ViewRegistry,
|
ViewRegistry,
|
||||||
ImageryPlugin,
|
ImageryPlugin,
|
||||||
InspectorViewRegistry,
|
InspectorViewRegistry,
|
||||||
ToolbarRegistry,
|
ToolbarRegistry,
|
||||||
ApplicationRouter,
|
ApplicationRouter,
|
||||||
Browse,
|
Browse,
|
||||||
Main,
|
|
||||||
Layout,
|
Layout,
|
||||||
DomainObjectImpl,
|
|
||||||
ContextualDomainObject,
|
|
||||||
PreviewPlugin,
|
PreviewPlugin,
|
||||||
BrandingAPI,
|
BrandingAPI,
|
||||||
LicensesPlugin,
|
LicensesPlugin,
|
||||||
@ -108,23 +92,6 @@ define([
|
|||||||
revision: __OPENMCT_REVISION__,
|
revision: __OPENMCT_REVISION__,
|
||||||
branch: __OPENMCT_BUILD_BRANCH__
|
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);
|
this.destroy = this.destroy.bind(this);
|
||||||
/**
|
/**
|
||||||
@ -264,16 +231,12 @@ define([
|
|||||||
|
|
||||||
this.branding = BrandingAPI.default;
|
this.branding = BrandingAPI.default;
|
||||||
|
|
||||||
this.legacyRegistry = new BundleRegistry();
|
|
||||||
installDefaultBundles(this.legacyRegistry);
|
|
||||||
|
|
||||||
// Plugins that are installed by default
|
// Plugins that are installed by default
|
||||||
|
|
||||||
this.install(this.plugins.Plot());
|
this.install(this.plugins.Plot());
|
||||||
this.install(this.plugins.Chart());
|
this.install(this.plugins.Chart());
|
||||||
this.install(this.plugins.TelemetryTable.default());
|
this.install(this.plugins.TelemetryTable.default());
|
||||||
this.install(PreviewPlugin.default());
|
this.install(PreviewPlugin.default());
|
||||||
this.install(LegacyIndicatorsPlugin());
|
|
||||||
this.install(LicensesPlugin.default());
|
this.install(LicensesPlugin.default());
|
||||||
this.install(RemoveActionPlugin.default());
|
this.install(RemoveActionPlugin.default());
|
||||||
this.install(MoveActionPlugin.default());
|
this.install(MoveActionPlugin.default());
|
||||||
@ -305,51 +268,6 @@ define([
|
|||||||
|
|
||||||
MCT.prototype.MCT = MCT;
|
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.
|
* Set path to where assets are hosted. This should be the path to main.js.
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
@ -395,25 +313,6 @@ define([
|
|||||||
|
|
||||||
this.element = domElement;
|
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.route(/^\/$/, () => {
|
||||||
this.router.setPath('/browse/');
|
this.router.setPath('/browse/');
|
||||||
});
|
});
|
||||||
@ -424,13 +323,6 @@ define([
|
|||||||
* @event start
|
* @event start
|
||||||
* @memberof module:openmct.MCT~
|
* @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) {
|
if (!isHeadlessMode) {
|
||||||
const appLayout = new Vue({
|
const appLayout = new Vue({
|
||||||
@ -452,7 +344,6 @@ define([
|
|||||||
|
|
||||||
this.router.start();
|
this.router.start();
|
||||||
this.emit('start');
|
this.emit('start');
|
||||||
}.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MCT.prototype.startHeadless = function () {
|
MCT.prototype.startHeadless = function () {
|
||||||
|
@ -22,21 +22,18 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'./plugins/plugins',
|
'./plugins/plugins',
|
||||||
'legacyRegistry',
|
|
||||||
'utils/testing'
|
'utils/testing'
|
||||||
], function (plugins, legacyRegistry, testUtils) {
|
], function (plugins, testUtils) {
|
||||||
describe("MCT", function () {
|
describe("MCT", function () {
|
||||||
let openmct;
|
let openmct;
|
||||||
let mockPlugin;
|
let mockPlugin;
|
||||||
let mockPlugin2;
|
let mockPlugin2;
|
||||||
let mockListener;
|
let mockListener;
|
||||||
let oldBundles;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockPlugin = jasmine.createSpy('plugin');
|
mockPlugin = jasmine.createSpy('plugin');
|
||||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
mockPlugin2 = jasmine.createSpy('plugin2');
|
||||||
mockListener = jasmine.createSpy('listener');
|
mockListener = jasmine.createSpy('listener');
|
||||||
oldBundles = legacyRegistry.list();
|
|
||||||
|
|
||||||
openmct = testUtils.createOpenMct();
|
openmct = testUtils.createOpenMct();
|
||||||
|
|
||||||
@ -47,12 +44,6 @@ define([
|
|||||||
|
|
||||||
// Clean up the dirty singleton.
|
// Clean up the dirty singleton.
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
legacyRegistry.list().forEach(function (bundle) {
|
|
||||||
if (oldBundles.indexOf(bundle) === -1) {
|
|
||||||
legacyRegistry.delete(bundle);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return testUtils.resetApplicationState(openmct);
|
return testUtils.resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,10 +102,6 @@ define([
|
|||||||
describe("setAssetPath", function () {
|
describe("setAssetPath", function () {
|
||||||
let testAssetPath;
|
let testAssetPath;
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("configures the path for assets", function () {
|
it("configures the path for assets", function () {
|
||||||
testAssetPath = "some/path/";
|
testAssetPath = "some/path/";
|
||||||
openmct.setAssetPath(testAssetPath);
|
openmct.setAssetPath(testAssetPath);
|
||||||
|
@ -133,5 +133,9 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CompositionAPI.prototype.supportsComposition = function (domainObject) {
|
||||||
|
return this.get(domainObject) !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
return CompositionAPI;
|
return CompositionAPI;
|
||||||
});
|
});
|
||||||
|
@ -87,6 +87,12 @@ define([
|
|||||||
expect(composition).toEqual(jasmine.any(CompositionCollection));
|
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 () {
|
it('loads composition from domain object', function () {
|
||||||
const listener = jasmine.createSpy('addListener');
|
const listener = jasmine.createSpy('addListener');
|
||||||
composition.on('add', listener);
|
composition.on('add', listener);
|
||||||
|
@ -49,8 +49,10 @@ define([
|
|||||||
this.onMutation = this.onMutation.bind(this);
|
this.onMutation = this.onMutation.bind(this);
|
||||||
|
|
||||||
this.cannotContainItself = this.cannotContainItself.bind(this);
|
this.cannotContainItself = this.cannotContainItself.bind(this);
|
||||||
|
this.supportsComposition = this.supportsComposition.bind(this);
|
||||||
|
|
||||||
compositionAPI.addPolicy(this.cannotContainItself);
|
compositionAPI.addPolicy(this.cannotContainItself);
|
||||||
|
compositionAPI.addPolicy(this.supportsComposition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,6 +63,13 @@ define([
|
|||||||
&& parent.identifier.key === child.identifier.key);
|
&& 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
|
* Check if this provider should be used to load composition for a
|
||||||
* particular domain object.
|
* particular domain object.
|
||||||
|
@ -41,7 +41,7 @@ function ObjectAPI(typeRegistry, openmct) {
|
|||||||
this.typeRegistry = typeRegistry;
|
this.typeRegistry = typeRegistry;
|
||||||
this.eventEmitter = new EventEmitter();
|
this.eventEmitter = new EventEmitter();
|
||||||
this.providers = {};
|
this.providers = {};
|
||||||
this.rootRegistry = new RootRegistry();
|
this.rootRegistry = new RootRegistry(openmct);
|
||||||
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
|
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
|
||||||
|
|
||||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||||
@ -367,14 +367,17 @@ ObjectAPI.prototype.endTransaction = function () {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a root-level object.
|
* Add a root-level object.
|
||||||
* @param {module:openmct.ObjectAPI~Identifier|function} an array of
|
* @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or
|
||||||
* identifiers for root level objects, or a function that returns a
|
* an array of identifiers for root level objects, or a function that returns a
|
||||||
* promise for an identifier or an array of root level objects.
|
* promise for an identifier or an array of root level objects.
|
||||||
|
* @param {module:openmct.PriorityAPI~priority|Number} priority a number representing
|
||||||
|
* this item(s) position in the root object's composition (example: order in object tree).
|
||||||
|
* For arrays, they are treated as blocks.
|
||||||
* @method addRoot
|
* @method addRoot
|
||||||
* @memberof module:openmct.ObjectAPI#
|
* @memberof module:openmct.ObjectAPI#
|
||||||
*/
|
*/
|
||||||
ObjectAPI.prototype.addRoot = function (key) {
|
ObjectAPI.prototype.addRoot = function (identifier, priority) {
|
||||||
this.rootRegistry.addRoot(key);
|
this.rootRegistry.addRoot(identifier, priority);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,39 +20,43 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
import utils from './object-utils';
|
||||||
'lodash'
|
|
||||||
], function (
|
|
||||||
_
|
|
||||||
) {
|
|
||||||
|
|
||||||
function RootRegistry() {
|
export default class RootRegistry {
|
||||||
this.providers = [];
|
|
||||||
|
constructor(openmct) {
|
||||||
|
this._rootItems = [];
|
||||||
|
this._openmct = openmct;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootRegistry.prototype.getRoots = function () {
|
getRoots() {
|
||||||
const promises = this.providers.map(function (provider) {
|
const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority);
|
||||||
return provider();
|
const promises = sortedItems.map((rootItem) => rootItem.provider());
|
||||||
|
|
||||||
|
return Promise.all(promises).then(rootItems => rootItems.flat());
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoot(rootItem, priority) {
|
||||||
|
|
||||||
|
if (!this._isValid(rootItem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._rootItems.push({
|
||||||
|
priority: priority || this._openmct.priority.DEFAULT,
|
||||||
|
provider: typeof rootItem === 'function' ? rootItem : () => rootItem
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
.then(_.flatten);
|
|
||||||
};
|
|
||||||
|
|
||||||
function isKey(key) {
|
|
||||||
return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RootRegistry.prototype.addRoot = function (key) {
|
_isValid(rootItem) {
|
||||||
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
|
if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') {
|
||||||
this.providers.push(function () {
|
return true;
|
||||||
return key;
|
|
||||||
});
|
|
||||||
} else if (typeof key === "function") {
|
|
||||||
this.providers.push(key);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return RootRegistry;
|
if (Array.isArray(rootItem)) {
|
||||||
|
return rootItem.every(utils.isIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -172,6 +172,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
isIdentifier: isIdentifier,
|
||||||
toOldFormat: toOldFormat,
|
toOldFormat: toOldFormat,
|
||||||
toNewFormat: toNewFormat,
|
toNewFormat: toNewFormat,
|
||||||
makeKeyString: makeKeyString,
|
makeKeyString: makeKeyString,
|
||||||
|
@ -19,18 +19,18 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
define([
|
|
||||||
'../RootRegistry'
|
import { createOpenMct, resetApplicationState } from '../../../utils/testing';
|
||||||
], function (
|
|
||||||
RootRegistry
|
describe('RootRegistry', () => {
|
||||||
) {
|
let openmct;
|
||||||
describe('RootRegistry', function () {
|
|
||||||
let idA;
|
let idA;
|
||||||
let idB;
|
let idB;
|
||||||
let idC;
|
let idC;
|
||||||
let registry;
|
let idD;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
idA = {
|
idA = {
|
||||||
key: 'keyA',
|
key: 'keyA',
|
||||||
namespace: 'something'
|
namespace: 'something'
|
||||||
@ -43,59 +43,89 @@ define([
|
|||||||
key: 'keyC',
|
key: 'keyC',
|
||||||
namespace: 'something'
|
namespace: 'something'
|
||||||
};
|
};
|
||||||
registry = new RootRegistry();
|
idD = {
|
||||||
|
key: 'keyD',
|
||||||
|
namespace: 'something'
|
||||||
|
};
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can register a root by key', function () {
|
afterEach(async () => {
|
||||||
registry.addRoot(idA);
|
await resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
it('can register a root by identifier', () => {
|
||||||
.then(function (roots) {
|
openmct.objects.addRoot(idA);
|
||||||
expect(roots).toEqual([idA]);
|
|
||||||
|
return openmct.objects.getRoot()
|
||||||
|
.then((rootObject) => {
|
||||||
|
expect(rootObject.composition).toEqual([idA]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can register multiple roots by key', function () {
|
it('can register multiple roots by identifier', () => {
|
||||||
registry.addRoot([idA, idB]);
|
openmct.objects.addRoot([idA, idB]);
|
||||||
|
|
||||||
return registry.getRoots()
|
return openmct.objects.getRoot()
|
||||||
.then(function (roots) {
|
.then((rootObject) => {
|
||||||
expect(roots).toEqual([idA, idB]);
|
expect(rootObject.composition).toEqual([idA, idB]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can register an asynchronous root ', function () {
|
it('can register an asynchronous root ', () => {
|
||||||
registry.addRoot(function () {
|
openmct.objects.addRoot(() => Promise.resolve(idA));
|
||||||
return Promise.resolve(idA);
|
|
||||||
});
|
|
||||||
|
|
||||||
return registry.getRoots()
|
return openmct.objects.getRoot()
|
||||||
.then(function (roots) {
|
.then((rootObject) => {
|
||||||
expect(roots).toEqual([idA]);
|
expect(rootObject.composition).toEqual([idA]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can register multiple asynchronous roots', function () {
|
it('can register multiple asynchronous roots', () => {
|
||||||
registry.addRoot(function () {
|
openmct.objects.addRoot(() => Promise.resolve([idA, idB]));
|
||||||
return Promise.resolve([idA, idB]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return registry.getRoots()
|
return openmct.objects.getRoot()
|
||||||
.then(function (roots) {
|
.then((rootObject) => {
|
||||||
expect(roots).toEqual([idA, idB]);
|
expect(rootObject.composition).toEqual([idA, idB]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can combine different types of registration', function () {
|
it('can combine different types of registration', () => {
|
||||||
registry.addRoot([idA, idB]);
|
openmct.objects.addRoot([idA, idB]);
|
||||||
registry.addRoot(function () {
|
openmct.objects.addRoot(() => Promise.resolve([idC]));
|
||||||
return Promise.resolve([idC]);
|
|
||||||
|
return openmct.objects.getRoot()
|
||||||
|
.then((rootObject) => {
|
||||||
|
expect(rootObject.composition).toEqual([idA, idB, idC]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
it('supports priority ordering for identifiers', () => {
|
||||||
.then(function (roots) {
|
openmct.objects.addRoot(idA, openmct.priority.LOW);
|
||||||
expect(roots).toEqual([idA, idB, idC]);
|
openmct.objects.addRoot(idB, openmct.priority.HIGH);
|
||||||
|
openmct.objects.addRoot(idC); // DEFAULT
|
||||||
|
|
||||||
|
return openmct.objects.getRoot()
|
||||||
|
.then((rootObject) => {
|
||||||
|
expect(rootObject.composition[0]).toEqual(idB);
|
||||||
|
expect(rootObject.composition[1]).toEqual(idC);
|
||||||
|
expect(rootObject.composition[2]).toEqual(idA);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports priority ordering for different types of registration', () => {
|
||||||
|
openmct.objects.addRoot(() => Promise.resolve([idC]), openmct.priority.LOW);
|
||||||
|
openmct.objects.addRoot(idB, openmct.priority.HIGH);
|
||||||
|
openmct.objects.addRoot([idA, idD]); // default
|
||||||
|
|
||||||
|
return openmct.objects.getRoot()
|
||||||
|
.then((rootObject) => {
|
||||||
|
expect(rootObject.composition[0]).toEqual(idB);
|
||||||
|
expect(rootObject.composition[1]).toEqual(idA);
|
||||||
|
expect(rootObject.composition[2]).toEqual(idD);
|
||||||
|
expect(rootObject.composition[3]).toEqual(idC);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,7 +31,7 @@ export default class LADTableViewProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canView(domainObject) {
|
canView(domainObject) {
|
||||||
const supportsComposition = this.openmct.composition.get(domainObject) !== undefined;
|
const supportsComposition = this.openmct.composition.supportsComposition(domainObject);
|
||||||
const providesTelemetry = this.openmct.telemetry.isTelemetryObject(domainObject);
|
const providesTelemetry = this.openmct.telemetry.isTelemetryObject(domainObject);
|
||||||
|
|
||||||
return domainObject.type === 'LadTable'
|
return domainObject.type === 'LadTable'
|
||||||
|
@ -62,12 +62,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.refreshData = this.refreshData.bind(this);
|
||||||
|
this.setTimeContext();
|
||||||
|
|
||||||
this.loadComposition();
|
this.loadComposition();
|
||||||
|
|
||||||
this.openmct.time.on('bounds', this.refreshData);
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.openmct.time.off('bounds', this.refreshData);
|
this.stopFollowingTimeContext();
|
||||||
|
|
||||||
this.removeAllSubscriptions();
|
this.removeAllSubscriptions();
|
||||||
|
|
||||||
@ -79,6 +81,21 @@ export default {
|
|||||||
this.composition.off('remove', this.removeTelemetryObject);
|
this.composition.off('remove', this.removeTelemetryObject);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setTimeContext() {
|
||||||
|
this.stopFollowingTimeContext();
|
||||||
|
|
||||||
|
this.timeContext = this.openmct.time.getContextForView(this.path);
|
||||||
|
this.followTimeContext();
|
||||||
|
|
||||||
|
},
|
||||||
|
followTimeContext() {
|
||||||
|
this.timeContext.on('bounds', this.refreshData);
|
||||||
|
},
|
||||||
|
stopFollowingTimeContext() {
|
||||||
|
if (this.timeContext) {
|
||||||
|
this.timeContext.off('bounds', this.refreshData);
|
||||||
|
}
|
||||||
|
},
|
||||||
addTelemetryObject(telemetryObject) {
|
addTelemetryObject(telemetryObject) {
|
||||||
// grab information we need from the added telmetry object
|
// grab information we need from the added telmetry object
|
||||||
const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
@ -147,7 +164,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
getOptions() {
|
getOptions() {
|
||||||
const { start, end } = this.openmct.time.bounds();
|
const { start, end } = this.timeContext.bounds();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
end,
|
end,
|
||||||
@ -247,10 +264,10 @@ export default {
|
|||||||
this.addTrace(trace, key);
|
this.addTrace(trace, key);
|
||||||
},
|
},
|
||||||
isDataInTimeRange(datum, key) {
|
isDataInTimeRange(datum, key) {
|
||||||
const timeSystemKey = this.openmct.time.timeSystem().key;
|
const timeSystemKey = this.timeContext.timeSystem().key;
|
||||||
let currentTimestamp = this.parse(key, timeSystemKey, datum);
|
let currentTimestamp = this.parse(key, timeSystemKey, datum);
|
||||||
|
|
||||||
return currentTimestamp && this.openmct.time.bounds().end >= currentTimestamp;
|
return currentTimestamp && this.timeContext.bounds().end >= currentTimestamp;
|
||||||
},
|
},
|
||||||
format(telemetryObjectKey, metadataKey, data) {
|
format(telemetryObjectKey, metadataKey, data) {
|
||||||
const formats = this.telemetryObjectFormats[telemetryObjectKey];
|
const formats = this.telemetryObjectFormats[telemetryObjectKey];
|
||||||
|
@ -130,14 +130,6 @@ describe("the plugin", function () {
|
|||||||
let mockComposition;
|
let mockComposition;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const getFunc = openmct.$injector.get;
|
|
||||||
spyOn(openmct.$injector, "get")
|
|
||||||
.withArgs("exportImageService").and.returnValue({
|
|
||||||
exportPNG: () => {},
|
|
||||||
exportJPG: () => {}
|
|
||||||
})
|
|
||||||
.and.callFake(getFunc);
|
|
||||||
|
|
||||||
barGraphObject = {
|
barGraphObject = {
|
||||||
identifier: {
|
identifier: {
|
||||||
namespace: "",
|
namespace: "",
|
||||||
|
@ -48,7 +48,7 @@ define([
|
|||||||
|
|
||||||
let indicator = {
|
let indicator = {
|
||||||
element: component.$mount().$el,
|
element: component.$mount().$el,
|
||||||
key: 'clear-data-indicator',
|
key: 'global-clear-indicator',
|
||||||
priority: openmct.priority.DEFAULT
|
priority: openmct.priority.DEFAULT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
228
src/plugins/clearData/pluginSpec.js
Normal file
228
src/plugins/clearData/pluginSpec.js
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 ClearDataPlugin from './plugin.js';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { createOpenMct, resetApplicationState, createMouseEvent } from 'utils/testing';
|
||||||
|
|
||||||
|
describe('The Clear Data Plugin:', () => {
|
||||||
|
let clearDataPlugin;
|
||||||
|
|
||||||
|
describe('The clear data action:', () => {
|
||||||
|
let openmct;
|
||||||
|
let selection;
|
||||||
|
let mockObjectPath;
|
||||||
|
let clearDataAction;
|
||||||
|
let testViewObject;
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
clearDataPlugin = new ClearDataPlugin(
|
||||||
|
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||||
|
{indicator: true}
|
||||||
|
);
|
||||||
|
openmct.install(clearDataPlugin);
|
||||||
|
|
||||||
|
clearDataAction = openmct.actions.getAction('clear-data-action');
|
||||||
|
testViewObject = [{
|
||||||
|
identifier: {
|
||||||
|
key: "foo-table",
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: "table"
|
||||||
|
}];
|
||||||
|
openmct.router.path = testViewObject;
|
||||||
|
mockObjectPath = [
|
||||||
|
{
|
||||||
|
name: 'Mock Table',
|
||||||
|
type: 'table',
|
||||||
|
identifier: {
|
||||||
|
key: "foo-table",
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
selection = [
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: mockObjectPath[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
openmct.router.path = null;
|
||||||
|
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
it('is installed', () => {
|
||||||
|
expect(clearDataAction).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is applicable on applicable objects', () => {
|
||||||
|
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
|
||||||
|
expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is not applicable on inapplicable objects', () => {
|
||||||
|
testViewObject = [{
|
||||||
|
identifier: {
|
||||||
|
key: "foo-widget",
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: "widget"
|
||||||
|
}];
|
||||||
|
mockObjectPath = [
|
||||||
|
{
|
||||||
|
name: 'Mock Widget',
|
||||||
|
type: 'widget',
|
||||||
|
identifier: {
|
||||||
|
key: "foo-widget",
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
selection = [
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: mockObjectPath[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
|
||||||
|
expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is not applicable if object not in the selection path and not a layout', () => {
|
||||||
|
selection = [
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: {
|
||||||
|
name: 'Some Random Widget',
|
||||||
|
type: 'not-in-path-widget',
|
||||||
|
identifier: {
|
||||||
|
key: "something-else-widget",
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
|
||||||
|
expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is applicable if object not in the selection path and is a layout', () => {
|
||||||
|
selection = [
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: {
|
||||||
|
name: 'Some Random Widget',
|
||||||
|
type: 'not-in-path-widget',
|
||||||
|
identifier: {
|
||||||
|
key: "something-else-widget",
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
openmct.selection.select(selection);
|
||||||
|
|
||||||
|
testViewObject = [{
|
||||||
|
identifier: {
|
||||||
|
key: "foo-layout",
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: "layout"
|
||||||
|
}];
|
||||||
|
openmct.router.path = testViewObject;
|
||||||
|
const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
|
||||||
|
expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fires an event upon invocation', (done) => {
|
||||||
|
openmct.objectViews.on('clearData', (domainObject) => {
|
||||||
|
expect(domainObject).toEqual(testViewObject[0]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
clearDataAction.invoke(testViewObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('The clear data indicator:', () => {
|
||||||
|
let openmct;
|
||||||
|
let appHolder;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
clearDataPlugin = new ClearDataPlugin(
|
||||||
|
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||||
|
{indicator: true}
|
||||||
|
);
|
||||||
|
openmct.install(clearDataPlugin);
|
||||||
|
appHolder = document.createElement('div');
|
||||||
|
document.body.appendChild(appHolder);
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.start(appHolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs', () => {
|
||||||
|
const globalClearIndicator = openmct.indicators.indicatorObjects
|
||||||
|
.find(indicator => indicator.key === 'global-clear-indicator').element;
|
||||||
|
expect(globalClearIndicator).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders its major elements", async () => {
|
||||||
|
await Vue.nextTick();
|
||||||
|
const indicatorClass = appHolder.querySelector('.c-indicator');
|
||||||
|
const iconClass = appHolder.querySelector('.icon-clear-data');
|
||||||
|
const indicatorLabel = appHolder.querySelector('.c-indicator__label');
|
||||||
|
const buttonElement = indicatorLabel.querySelector('button');
|
||||||
|
const hasMajorElements = Boolean(indicatorClass && iconClass && buttonElement);
|
||||||
|
|
||||||
|
expect(hasMajorElements).toBe(true);
|
||||||
|
expect(buttonElement.innerText).toEqual('Clear Data');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clicking the button fires the global clear", (done) => {
|
||||||
|
const indicatorLabel = appHolder.querySelector('.c-indicator__label');
|
||||||
|
const buttonElement = indicatorLabel.querySelector('button');
|
||||||
|
const clickEvent = createMouseEvent('click');
|
||||||
|
openmct.objectViews.on('clearData', () => {
|
||||||
|
// when we click the button, this event should fire
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
buttonElement.dispatchEvent(clickEvent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,143 +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 ClearDataActionPlugin from '../plugin.js';
|
|
||||||
import ClearDataAction from '../ClearDataAction.js';
|
|
||||||
|
|
||||||
describe('When the Clear Data Plugin is installed,', () => {
|
|
||||||
const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
|
|
||||||
const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
|
|
||||||
const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
|
|
||||||
const goodMockSelectionPath = [[{
|
|
||||||
context: {
|
|
||||||
item: {
|
|
||||||
identifier: {
|
|
||||||
key: 'apple',
|
|
||||||
namespace: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]];
|
|
||||||
|
|
||||||
const openmct = {
|
|
||||||
objectViews: mockObjectViews,
|
|
||||||
indicators: mockIndicatorProvider,
|
|
||||||
priority: {
|
|
||||||
DEFAULT: 0
|
|
||||||
},
|
|
||||||
actions: mockActionsProvider,
|
|
||||||
install: function (plugin) {
|
|
||||||
plugin(this);
|
|
||||||
},
|
|
||||||
selection: {
|
|
||||||
get: function () {
|
|
||||||
return goodMockSelectionPath;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
objects: {
|
|
||||||
areIdsEqual: function (obj1, obj2) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockObjectPath = [
|
|
||||||
{
|
|
||||||
name: 'mockObject1',
|
|
||||||
type: 'apple'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mockObject2',
|
|
||||||
type: 'banana'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
it('Global Clear Indicator is installed', () => {
|
|
||||||
openmct.install(ClearDataActionPlugin(openmct, {indicator: true}));
|
|
||||||
|
|
||||||
expect(mockIndicatorProvider.add).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Clear Data context menu action is installed', () => {
|
|
||||||
openmct.install(ClearDataActionPlugin(openmct, []));
|
|
||||||
|
|
||||||
expect(mockActionsProvider.register).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('clear data action emits a clearData event when invoked', () => {
|
|
||||||
const action = new ClearDataAction(openmct);
|
|
||||||
|
|
||||||
action.invoke(mockObjectPath);
|
|
||||||
|
|
||||||
expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('clears data on applicable objects', () => {
|
|
||||||
let action = new ClearDataAction(openmct, ['apple']);
|
|
||||||
|
|
||||||
const actionApplies = action.appliesTo(mockObjectPath);
|
|
||||||
|
|
||||||
expect(actionApplies).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not clear data on inapplicable objects', () => {
|
|
||||||
let action = new ClearDataAction(openmct, ['pineapple']);
|
|
||||||
|
|
||||||
const actionApplies = action.appliesTo(mockObjectPath);
|
|
||||||
|
|
||||||
expect(actionApplies).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not clear data if not in the selection path and not a layout', () => {
|
|
||||||
openmct.objects = {
|
|
||||||
areIdsEqual: function (obj1, obj2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
openmct.router = {
|
|
||||||
path: [{type: 'not-a-layout'}]
|
|
||||||
};
|
|
||||||
|
|
||||||
let action = new ClearDataAction(openmct, ['apple']);
|
|
||||||
|
|
||||||
const actionApplies = action.appliesTo(mockObjectPath);
|
|
||||||
|
|
||||||
expect(actionApplies).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does clear data if not in the selection path and is a layout', () => {
|
|
||||||
openmct.objects = {
|
|
||||||
areIdsEqual: function (obj1, obj2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
openmct.router = {
|
|
||||||
path: [{type: 'layout'}]
|
|
||||||
};
|
|
||||||
|
|
||||||
let action = new ClearDataAction(openmct, ['apple']);
|
|
||||||
|
|
||||||
const actionApplies = action.appliesTo(mockObjectPath);
|
|
||||||
|
|
||||||
expect(actionApplies).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
@ -87,6 +87,7 @@ describe("Clock plugin:", () => {
|
|||||||
|
|
||||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
|
||||||
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
|
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
|
||||||
|
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
|
||||||
|
|
||||||
const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
|
const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
|
||||||
clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
|
clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
|
||||||
|
@ -1,34 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
function ConditionSetViewPolicy() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
|
|
||||||
if (domainObject.getModel().type === 'conditionSet') {
|
|
||||||
return view.key === 'conditionSet.view';
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConditionSetViewPolicy;
|
|
@ -109,7 +109,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
||||||
this.initialize(styleConfiguration || {});
|
this.initialize(styleConfiguration || {});
|
||||||
this.applyStaticStyle();
|
this.applyStaticStyle();
|
||||||
this.destroy();
|
this.destroy(true);
|
||||||
} else {
|
} else {
|
||||||
let isNewConditionSet = !this.conditionSetIdentifier
|
let isNewConditionSet = !this.conditionSetIdentifier
|
||||||
|| !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
|
|| !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
|
||||||
@ -180,15 +180,17 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
this.updateDomainObjectStyle();
|
this.updateDomainObjectStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy(skipEventListeners) {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
|
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
delete this.stopProvidingTelemetry;
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipEventListeners) {
|
||||||
this.openmct.time.off("bounds", this.refreshData);
|
this.openmct.time.off("bounds", this.refreshData);
|
||||||
this.openmct.editor.off('isEditing', this.toggleSubscription);
|
this.openmct.editor.off('isEditing', this.toggleSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
this.conditionSetIdentifier = undefined;
|
this.conditionSetIdentifier = undefined;
|
||||||
}
|
}
|
||||||
|
@ -1,475 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="c-inspector__styles c-inspect-styles">
|
|
||||||
<template v-if="!conditionSetDomainObject">
|
|
||||||
<div class="c-inspect-styles__header">
|
|
||||||
Object Style
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-styles__content">
|
|
||||||
<div v-if="staticStyle"
|
|
||||||
class="c-inspect-styles__style"
|
|
||||||
>
|
|
||||||
<StyleEditor class="c-inspect-styles__editor"
|
|
||||||
:style-item="staticStyle"
|
|
||||||
:is-editing="isEditing"
|
|
||||||
@persist="updateStaticStyle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
id="addConditionSet"
|
|
||||||
class="c-button c-button--major c-toggle-styling-button labeled"
|
|
||||||
@click="addConditionSet"
|
|
||||||
>
|
|
||||||
<span class="c-cs-button__label">Use Conditional Styling...</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="c-inspect-styles__header">
|
|
||||||
Conditional Object Styles
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
|
||||||
<a v-if="conditionSetDomainObject"
|
|
||||||
class="c-object-label icon-conditional"
|
|
||||||
@click="navigateOrPreview"
|
|
||||||
>
|
|
||||||
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
|
||||||
</a>
|
|
||||||
<template v-if="isEditing">
|
|
||||||
<button
|
|
||||||
id="changeConditionSet"
|
|
||||||
class="c-button labeled"
|
|
||||||
@click="addConditionSet"
|
|
||||||
>
|
|
||||||
<span class="c-button__label">Change...</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="c-click-icon icon-x"
|
|
||||||
title="Remove conditional styles"
|
|
||||||
@click="removeConditionSet"
|
|
||||||
></button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="conditionsLoaded"
|
|
||||||
class="c-inspect-styles__conditions"
|
|
||||||
>
|
|
||||||
<div v-for="(conditionStyle, index) in conditionalStyles"
|
|
||||||
:key="index"
|
|
||||||
class="c-inspect-styles__condition"
|
|
||||||
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
|
|
||||||
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
|
|
||||||
>
|
|
||||||
<condition-error :show-label="true"
|
|
||||||
:condition="getCondition(conditionStyle.conditionId)"
|
|
||||||
/>
|
|
||||||
<condition-description :show-label="true"
|
|
||||||
:condition="getCondition(conditionStyle.conditionId)"
|
|
||||||
/>
|
|
||||||
<StyleEditor class="c-inspect-styles__editor"
|
|
||||||
:style-item="conditionStyle"
|
|
||||||
:is-editing="isEditing"
|
|
||||||
@persist="updateConditionalStyle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import StyleEditor from "./StyleEditor.vue";
|
|
||||||
import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
|
|
||||||
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
|
|
||||||
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
|
||||||
import Vue from 'vue';
|
|
||||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
|
||||||
import { getApplicableStylesForItem } from "@/plugins/condition/utils/styleUtils";
|
|
||||||
import isEmpty from 'lodash/isEmpty';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ConditionalStylesView',
|
|
||||||
components: {
|
|
||||||
ConditionDescription,
|
|
||||||
ConditionError,
|
|
||||||
StyleEditor
|
|
||||||
},
|
|
||||||
inject: [
|
|
||||||
'openmct',
|
|
||||||
'selection'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
conditionalStyles: [],
|
|
||||||
staticStyle: undefined,
|
|
||||||
conditionSetDomainObject: undefined,
|
|
||||||
isEditing: this.openmct.editor.isEditing(),
|
|
||||||
conditions: undefined,
|
|
||||||
conditionsLoaded: false,
|
|
||||||
navigateToPath: '',
|
|
||||||
selectedConditionId: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.removeListeners();
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.itemId = '';
|
|
||||||
this.getDomainObjectFromSelection();
|
|
||||||
this.previewAction = new PreviewAction(this.openmct);
|
|
||||||
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
|
||||||
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
|
|
||||||
this.initializeStaticStyle(objectStyles);
|
|
||||||
if (objectStyles && objectStyles.conditionSetIdentifier) {
|
|
||||||
this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
|
|
||||||
this.conditionalStyles = objectStyles.styles;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.initializeStaticStyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openmct.editor.on('isEditing', this.setEditState);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isItemType(type, item) {
|
|
||||||
return item && (item.type === type);
|
|
||||||
},
|
|
||||||
getDomainObjectFromSelection() {
|
|
||||||
let layoutItem;
|
|
||||||
let domainObject;
|
|
||||||
|
|
||||||
if (this.selection[0].length > 1) {
|
|
||||||
//If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
|
|
||||||
//The second item in the this.selection[0] list is the container object (usually a layout)
|
|
||||||
layoutItem = this.selection[0][0].context.layoutItem;
|
|
||||||
const item = this.selection[0][0].context.item;
|
|
||||||
this.canHide = true;
|
|
||||||
if (item
|
|
||||||
&& (!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
|
|
||||||
domainObject = item;
|
|
||||||
} else {
|
|
||||||
domainObject = this.selection[0][1].context.item;
|
|
||||||
if (layoutItem) {
|
|
||||||
this.itemId = layoutItem.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
domainObject = this.selection[0][0].context.item;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domainObject = domainObject;
|
|
||||||
this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.removeListeners();
|
|
||||||
if (this.domainObject) {
|
|
||||||
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
|
||||||
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
removeListeners() {
|
|
||||||
if (this.stopObserving) {
|
|
||||||
this.stopObserving();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stopObservingItems) {
|
|
||||||
this.stopObservingItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stopProvidingTelemetry) {
|
|
||||||
this.stopProvidingTelemetry();
|
|
||||||
delete this.stopProvidingTelemetry;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initialize(conditionSetDomainObject) {
|
|
||||||
//If there are new conditions in the conditionSet we need to set those styles to default
|
|
||||||
this.conditionSetDomainObject = conditionSetDomainObject;
|
|
||||||
this.enableConditionSetNav();
|
|
||||||
this.initializeConditionalStyles();
|
|
||||||
},
|
|
||||||
setEditState(isEditing) {
|
|
||||||
this.isEditing = isEditing;
|
|
||||||
if (this.isEditing) {
|
|
||||||
if (this.stopProvidingTelemetry) {
|
|
||||||
this.stopProvidingTelemetry();
|
|
||||||
delete this.stopProvidingTelemetry;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.subscribeToConditionSet();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addConditionSet() {
|
|
||||||
let conditionSetDomainObject;
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
function handleItemSelection({ item }) {
|
|
||||||
if (item) {
|
|
||||||
conditionSetDomainObject = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dismissDialog(overlay, initialize) {
|
|
||||||
overlay.dismiss();
|
|
||||||
if (initialize && conditionSetDomainObject) {
|
|
||||||
self.conditionSetDomainObject = conditionSetDomainObject;
|
|
||||||
self.conditionalStyles = [];
|
|
||||||
self.initializeConditionalStyles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vm = new Vue({
|
|
||||||
components: { SelectorDialogTree },
|
|
||||||
provide: {
|
|
||||||
openmct: this.openmct
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
handleItemSelection,
|
|
||||||
title: 'Select Condition Set'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
template: '<selector-dialog-tree :title="title" @treeItemSelected="handleItemSelection"></selector-dialog-tree>'
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
let overlay = this.openmct.overlays.overlay({
|
|
||||||
element: vm.$el,
|
|
||||||
size: 'small',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: 'OK',
|
|
||||||
emphasis: 'true',
|
|
||||||
callback: () => dismissDialog(overlay, true)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Cancel',
|
|
||||||
callback: () => dismissDialog(overlay, false)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
onDestroy: () => vm.$destroy()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
enableConditionSetNav() {
|
|
||||||
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
|
||||||
(objectPath) => {
|
|
||||||
this.objectPath = objectPath;
|
|
||||||
this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
navigateOrPreview(event) {
|
|
||||||
// If editing, display condition set in Preview overlay; otherwise nav to it while browsing
|
|
||||||
if (this.openmct.editor.isEditing()) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.previewAction.invoke(this.objectPath);
|
|
||||||
} else {
|
|
||||||
this.openmct.router.navigate(this.navigateToPath);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeConditionSet() {
|
|
||||||
this.conditionSetDomainObject = undefined;
|
|
||||||
this.conditionalStyles = [];
|
|
||||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
|
||||||
if (this.itemId) {
|
|
||||||
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
|
|
||||||
domainObjectStyles[this.itemId].selectedConditionId = undefined;
|
|
||||||
domainObjectStyles[this.itemId].defaultConditionId = undefined;
|
|
||||||
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
|
|
||||||
domainObjectStyles[this.itemId].styles = undefined;
|
|
||||||
delete domainObjectStyles[this.itemId].styles;
|
|
||||||
if (isEmpty(domainObjectStyles[this.itemId])) {
|
|
||||||
delete domainObjectStyles[this.itemId];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
domainObjectStyles.conditionSetIdentifier = undefined;
|
|
||||||
domainObjectStyles.selectedConditionId = undefined;
|
|
||||||
domainObjectStyles.defaultConditionId = undefined;
|
|
||||||
delete domainObjectStyles.conditionSetIdentifier;
|
|
||||||
domainObjectStyles.styles = undefined;
|
|
||||||
delete domainObjectStyles.styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty(domainObjectStyles)) {
|
|
||||||
domainObjectStyles = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.persist(domainObjectStyles);
|
|
||||||
if (this.stopProvidingTelemetry) {
|
|
||||||
this.stopProvidingTelemetry();
|
|
||||||
delete this.stopProvidingTelemetry;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateDomainObjectItemStyles(newItems) {
|
|
||||||
//check that all items that have been styles still exist. Otherwise delete those styles
|
|
||||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
|
||||||
let itemsToRemove = [];
|
|
||||||
let keys = Object.keys(domainObjectStyles);
|
|
||||||
//TODO: Need an easier way to find which properties are itemIds
|
|
||||||
keys.forEach((key) => {
|
|
||||||
const keyIsItemId = (key !== 'styles')
|
|
||||||
&& (key !== 'staticStyle')
|
|
||||||
&& (key !== 'defaultConditionId')
|
|
||||||
&& (key !== 'selectedConditionId')
|
|
||||||
&& (key !== 'conditionSetIdentifier');
|
|
||||||
if (keyIsItemId) {
|
|
||||||
if (!(newItems.find(item => item.id === key))) {
|
|
||||||
itemsToRemove.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (itemsToRemove.length) {
|
|
||||||
this.removeItemStyles(itemsToRemove, domainObjectStyles);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeItemStyles(itemIds, domainObjectStyles) {
|
|
||||||
itemIds.forEach(itemId => {
|
|
||||||
if (domainObjectStyles[itemId]) {
|
|
||||||
domainObjectStyles[itemId] = undefined;
|
|
||||||
delete domainObjectStyles[this.itemId];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (isEmpty(domainObjectStyles)) {
|
|
||||||
domainObjectStyles = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.persist(domainObjectStyles);
|
|
||||||
},
|
|
||||||
initializeConditionalStyles() {
|
|
||||||
if (!this.conditions) {
|
|
||||||
this.conditions = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let conditionalStyles = [];
|
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
|
||||||
if (conditionConfiguration.isDefault) {
|
|
||||||
this.selectedConditionId = conditionConfiguration.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
|
||||||
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
|
||||||
if (foundStyle) {
|
|
||||||
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
|
|
||||||
conditionalStyles.push(foundStyle);
|
|
||||||
} else {
|
|
||||||
conditionalStyles.splice(index, 0, {
|
|
||||||
conditionId: conditionConfiguration.id,
|
|
||||||
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
|
|
||||||
this.conditionalStyles = conditionalStyles;
|
|
||||||
this.conditionsLoaded = true;
|
|
||||||
this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
|
|
||||||
if (!this.isEditing) {
|
|
||||||
this.subscribeToConditionSet();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subscribeToConditionSet() {
|
|
||||||
if (this.stopProvidingTelemetry) {
|
|
||||||
this.stopProvidingTelemetry();
|
|
||||||
delete this.stopProvidingTelemetry;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.conditionSetDomainObject) {
|
|
||||||
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
|
||||||
.then(output => {
|
|
||||||
if (output && output.length) {
|
|
||||||
this.handleConditionSetResultUpdated(output[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleConditionSetResultUpdated(resultData) {
|
|
||||||
this.selectedConditionId = resultData ? resultData.conditionId : '';
|
|
||||||
},
|
|
||||||
initializeStaticStyle(objectStyles) {
|
|
||||||
let staticStyle = objectStyles && objectStyles.staticStyle;
|
|
||||||
if (staticStyle) {
|
|
||||||
this.staticStyle = {
|
|
||||||
style: Object.assign({}, this.initialStyles, staticStyle.style)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.staticStyle = {
|
|
||||||
style: Object.assign({}, this.initialStyles)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
findStyleByConditionId(id) {
|
|
||||||
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
|
||||||
},
|
|
||||||
updateStaticStyle(staticStyle) {
|
|
||||||
this.staticStyle = staticStyle;
|
|
||||||
this.persist(this.getDomainObjectConditionalStyle());
|
|
||||||
},
|
|
||||||
updateConditionalStyle(conditionStyle) {
|
|
||||||
let found = this.findStyleByConditionId(conditionStyle.conditionId);
|
|
||||||
if (found) {
|
|
||||||
found.style = conditionStyle.style;
|
|
||||||
this.selectedConditionId = found.conditionId;
|
|
||||||
this.persist(this.getDomainObjectConditionalStyle());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getDomainObjectConditionalStyle(defaultConditionId) {
|
|
||||||
let objectStyle = {
|
|
||||||
styles: this.conditionalStyles,
|
|
||||||
staticStyle: this.staticStyle,
|
|
||||||
selectedConditionId: this.selectedConditionId
|
|
||||||
};
|
|
||||||
if (defaultConditionId) {
|
|
||||||
objectStyle.defaultConditionId = defaultConditionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.conditionSetDomainObject) {
|
|
||||||
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
|
||||||
|
|
||||||
if (this.itemId) {
|
|
||||||
domainObjectStyles[this.itemId] = objectStyle;
|
|
||||||
} else {
|
|
||||||
//we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
|
|
||||||
domainObjectStyles = {
|
|
||||||
...domainObjectStyles,
|
|
||||||
...objectStyle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return domainObjectStyles;
|
|
||||||
},
|
|
||||||
getCondition(id) {
|
|
||||||
return this.conditions ? this.conditions[id] : {};
|
|
||||||
},
|
|
||||||
applySelectedConditionStyle(conditionId) {
|
|
||||||
this.selectedConditionId = conditionId;
|
|
||||||
this.persist(this.getDomainObjectConditionalStyle());
|
|
||||||
},
|
|
||||||
persist(style) {
|
|
||||||
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
@ -1,280 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="c-inspector__styles c-inspect-styles">
|
|
||||||
<div class="c-inspect-styles__header">
|
|
||||||
Object Style
|
|
||||||
</div>
|
|
||||||
<div class="c-inspect-styles__content">
|
|
||||||
<div v-if="isStaticAndConditionalStyles"
|
|
||||||
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
|
|
||||||
>
|
|
||||||
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
|
|
||||||
</div>
|
|
||||||
<div v-if="staticStyle"
|
|
||||||
class="c-inspect-styles__style"
|
|
||||||
>
|
|
||||||
<style-editor class="c-inspect-styles__editor"
|
|
||||||
:style-item="staticStyle"
|
|
||||||
:is-editing="isEditing"
|
|
||||||
:mixed-styles="mixedStyles"
|
|
||||||
@persist="updateStaticStyle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import StyleEditor from "./StyleEditor.vue";
|
|
||||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
|
||||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
|
|
||||||
import isEmpty from 'lodash/isEmpty';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'MultiSelectStylesView',
|
|
||||||
components: {
|
|
||||||
StyleEditor
|
|
||||||
},
|
|
||||||
inject: [
|
|
||||||
'openmct',
|
|
||||||
'selection'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
staticStyle: undefined,
|
|
||||||
isEditing: this.openmct.editor.isEditing(),
|
|
||||||
mixedStyles: [],
|
|
||||||
isStaticAndConditionalStyles: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.removeListeners();
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.items = [];
|
|
||||||
this.previewAction = new PreviewAction(this.openmct);
|
|
||||||
this.getObjectsAndItemsFromSelection();
|
|
||||||
this.initializeStaticStyle();
|
|
||||||
this.openmct.editor.on('isEditing', this.setEditState);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isItemType(type, item) {
|
|
||||||
return item && (item.type === type);
|
|
||||||
},
|
|
||||||
hasConditionalStyles(domainObject, id) {
|
|
||||||
return getConditionalStyleForItem(domainObject, id) !== undefined;
|
|
||||||
},
|
|
||||||
getObjectsAndItemsFromSelection() {
|
|
||||||
let domainObject;
|
|
||||||
let subObjects = [];
|
|
||||||
|
|
||||||
//multiple selection
|
|
||||||
let itemInitialStyles = [];
|
|
||||||
let itemStyle;
|
|
||||||
this.selection.forEach((selectionItem) => {
|
|
||||||
const item = selectionItem[0].context.item;
|
|
||||||
const layoutItem = selectionItem[0].context.layoutItem;
|
|
||||||
if (item && this.isItemType('subobject-view', layoutItem)) {
|
|
||||||
subObjects.push(item);
|
|
||||||
itemStyle = getApplicableStylesForItem(item);
|
|
||||||
if (!this.isStaticAndConditionalStyles) {
|
|
||||||
this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
domainObject = selectionItem[1].context.item;
|
|
||||||
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
|
|
||||||
this.items.push({
|
|
||||||
id: layoutItem.id,
|
|
||||||
applicableStyles: itemStyle
|
|
||||||
});
|
|
||||||
if (!this.isStaticAndConditionalStyles) {
|
|
||||||
this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
itemInitialStyles.push(itemStyle);
|
|
||||||
});
|
|
||||||
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
|
|
||||||
this.initialStyles = styles;
|
|
||||||
this.mixedStyles = mixedStyles;
|
|
||||||
|
|
||||||
this.domainObject = domainObject;
|
|
||||||
this.removeListeners();
|
|
||||||
if (this.domainObject) {
|
|
||||||
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
|
||||||
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
|
||||||
}
|
|
||||||
|
|
||||||
subObjects.forEach(this.registerListener);
|
|
||||||
},
|
|
||||||
updateDomainObjectItemStyles(newItems) {
|
|
||||||
//check that all items that have been styles still exist. Otherwise delete those styles
|
|
||||||
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
|
|
||||||
keys.forEach((key) => {
|
|
||||||
if ((key !== 'styles')
|
|
||||||
&& (key !== 'staticStyle')
|
|
||||||
&& (key !== 'conditionSetIdentifier')) {
|
|
||||||
if (!(newItems.find(item => item.id === key))) {
|
|
||||||
this.removeItemStyles(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
registerListener(domainObject) {
|
|
||||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
|
||||||
|
|
||||||
if (!this.domainObjectsById) {
|
|
||||||
this.domainObjectsById = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.domainObjectsById[id]) {
|
|
||||||
this.domainObjectsById[id] = domainObject;
|
|
||||||
this.observeObject(domainObject, id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
observeObject(domainObject, id) {
|
|
||||||
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
|
|
||||||
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
|
|
||||||
}.bind(this));
|
|
||||||
this.unObserveObjects.push(unobserveObject);
|
|
||||||
},
|
|
||||||
removeListeners() {
|
|
||||||
if (this.stopObserving) {
|
|
||||||
this.stopObserving();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stopObservingItems) {
|
|
||||||
this.stopObservingItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.unObserveObjects) {
|
|
||||||
this.unObserveObjects.forEach((unObserveObject) => {
|
|
||||||
unObserveObject();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unObserveObjects = [];
|
|
||||||
},
|
|
||||||
removeItemStyles(itemId) {
|
|
||||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
|
||||||
if (itemId && domainObjectStyles[itemId]) {
|
|
||||||
domainObjectStyles[itemId] = undefined;
|
|
||||||
delete domainObjectStyles[this.itemId];
|
|
||||||
|
|
||||||
if (isEmpty(domainObjectStyles)) {
|
|
||||||
domainObjectStyles = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.persist(this.domainObject, domainObjectStyles);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeConditionalStyles(domainObjectStyles, itemId) {
|
|
||||||
if (itemId) {
|
|
||||||
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
|
|
||||||
delete domainObjectStyles[itemId].conditionSetIdentifier;
|
|
||||||
domainObjectStyles[itemId].styles = undefined;
|
|
||||||
delete domainObjectStyles[itemId].styles;
|
|
||||||
} else {
|
|
||||||
domainObjectStyles.conditionSetIdentifier = undefined;
|
|
||||||
delete domainObjectStyles.conditionSetIdentifier;
|
|
||||||
domainObjectStyles.styles = undefined;
|
|
||||||
delete domainObjectStyles.styles;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setEditState(isEditing) {
|
|
||||||
this.isEditing = isEditing;
|
|
||||||
},
|
|
||||||
initializeStaticStyle() {
|
|
||||||
this.staticStyle = {
|
|
||||||
style: Object.assign({}, this.initialStyles)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
updateStaticStyle(staticStyle, property) {
|
|
||||||
//update the static style for each of the layoutItems as well as each sub object item
|
|
||||||
this.staticStyle = staticStyle;
|
|
||||||
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
|
|
||||||
if (this.domainObjectsById) {
|
|
||||||
const keys = Object.keys(this.domainObjectsById);
|
|
||||||
keys.forEach(key => {
|
|
||||||
let domainObject = this.domainObjectsById[key];
|
|
||||||
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isStaticAndConditionalStyles = false;
|
|
||||||
let foundIndex = this.mixedStyles.indexOf(property);
|
|
||||||
if (foundIndex > -1) {
|
|
||||||
this.mixedStyles.splice(foundIndex, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getDomainObjectStyle(domainObject, property, items) {
|
|
||||||
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
|
||||||
|
|
||||||
if (items) {
|
|
||||||
items.forEach(item => {
|
|
||||||
let itemStaticStyle = {};
|
|
||||||
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
|
|
||||||
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(item.applicableStyles).forEach(key => {
|
|
||||||
if (property === key) {
|
|
||||||
itemStaticStyle[key] = this.staticStyle.style[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (this.isStaticAndConditionalStyles) {
|
|
||||||
this.removeConditionalStyles(domainObjectStyles, item.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty(itemStaticStyle)) {
|
|
||||||
itemStaticStyle = undefined;
|
|
||||||
domainObjectStyles[item.id] = undefined;
|
|
||||||
} else {
|
|
||||||
domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!domainObjectStyles.staticStyle) {
|
|
||||||
domainObjectStyles.staticStyle = {
|
|
||||||
style: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isStaticAndConditionalStyles) {
|
|
||||||
this.removeConditionalStyles(domainObjectStyles);
|
|
||||||
}
|
|
||||||
|
|
||||||
domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
|
|
||||||
}
|
|
||||||
|
|
||||||
return domainObjectStyles;
|
|
||||||
},
|
|
||||||
|
|
||||||
persist(domainObject, style) {
|
|
||||||
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -63,7 +63,7 @@
|
|||||||
<div class="c-inspect-styles__header">
|
<div class="c-inspect-styles__header">
|
||||||
Conditional Object Styles
|
Conditional Object Styles
|
||||||
</div>
|
</div>
|
||||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
<div class="c-inspect-styles__content c-inspect-styles__condition-set c-inspect-styles__elem">
|
||||||
<a v-if="conditionSetDomainObject"
|
<a v-if="conditionSetDomainObject"
|
||||||
class="c-object-label"
|
class="c-object-label"
|
||||||
@click="navigateOrPreview"
|
@click="navigateOrPreview"
|
||||||
@ -87,6 +87,27 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isConditionWidget && allowEditing"
|
||||||
|
class="c-inspect-styles__elem c-inspect-styles__output-label-toggle"
|
||||||
|
>
|
||||||
|
<label class="c-toggle-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="useConditionSetOutputAsLabel"
|
||||||
|
@change="updateConditionSetOutputLabel"
|
||||||
|
>
|
||||||
|
<span class="c-toggle-switch__slider"></span>
|
||||||
|
<span class="c-toggle-switch__label">Use Condition Set output as label</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div v-if="isConditionWidget && !allowEditing"
|
||||||
|
class="c-inspect-styles__elem"
|
||||||
|
>
|
||||||
|
<span class="c-toggle-switch__label">Condition Set output as label:
|
||||||
|
<span v-if="useConditionSetOutputAsLabel"> Yes</span><span v-else> No</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<FontStyleEditor
|
<FontStyleEditor
|
||||||
v-if="canStyleFont"
|
v-if="canStyleFont"
|
||||||
:font-style="consolidatedFontStyle"
|
:font-style="consolidatedFontStyle"
|
||||||
@ -172,7 +193,8 @@ export default {
|
|||||||
selectedConditionId: '',
|
selectedConditionId: '',
|
||||||
items: [],
|
items: [],
|
||||||
domainObject: undefined,
|
domainObject: undefined,
|
||||||
consolidatedFontStyle: {}
|
consolidatedFontStyle: {},
|
||||||
|
useConditionSetOutputAsLabel: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -187,6 +209,11 @@ export default {
|
|||||||
allowEditing() {
|
allowEditing() {
|
||||||
return this.isEditing && !this.locked;
|
return this.isEditing && !this.locked;
|
||||||
},
|
},
|
||||||
|
isConditionWidget() {
|
||||||
|
const hasConditionWidgetObjects = this.domainObjectsById && Object.values(this.domainObjectsById).some((object) => object.type === 'conditionWidget');
|
||||||
|
|
||||||
|
return (hasConditionWidgetObjects || (this.domainObject && this.domainObject.type === 'conditionWidget'));
|
||||||
|
},
|
||||||
styleableFontItems() {
|
styleableFontItems() {
|
||||||
return this.selection.filter(selectionPath => {
|
return this.selection.filter(selectionPath => {
|
||||||
const item = selectionPath[0].context.item;
|
const item = selectionPath[0].context.item;
|
||||||
@ -205,28 +232,6 @@ export default {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
computedconsolidatedFontStyle() {
|
|
||||||
let consolidatedFontStyle;
|
|
||||||
const styles = [];
|
|
||||||
|
|
||||||
this.styleableFontItems.forEach(styleable => {
|
|
||||||
const fontStyle = this.getFontStyle(styleable[0]);
|
|
||||||
|
|
||||||
styles.push(fontStyle);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (styles.length) {
|
|
||||||
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
|
|
||||||
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
|
|
||||||
|
|
||||||
consolidatedFontStyle = {
|
|
||||||
fontSize: hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC,
|
|
||||||
font: hasConsolidatedFont ? styles[0].font : NON_SPECIFIC
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return consolidatedFontStyle;
|
|
||||||
},
|
|
||||||
nonSpecificFontProperties() {
|
nonSpecificFontProperties() {
|
||||||
if (!this.consolidatedFontStyle) {
|
if (!this.consolidatedFontStyle) {
|
||||||
return [];
|
return [];
|
||||||
@ -247,6 +252,8 @@ export default {
|
|||||||
this.previewAction = new PreviewAction(this.openmct);
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
this.isMultipleSelection = this.selection.length > 1;
|
this.isMultipleSelection = this.selection.length > 1;
|
||||||
this.getObjectsAndItemsFromSelection();
|
this.getObjectsAndItemsFromSelection();
|
||||||
|
this.useConditionSetOutputAsLabel = this.getConfigurationForLabel();
|
||||||
|
|
||||||
if (!this.isMultipleSelection) {
|
if (!this.isMultipleSelection) {
|
||||||
let objectStyles = this.getObjectStyles();
|
let objectStyles = this.getObjectStyles();
|
||||||
this.initializeStaticStyle(objectStyles);
|
this.initializeStaticStyle(objectStyles);
|
||||||
@ -264,6 +271,12 @@ export default {
|
|||||||
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
|
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getConfigurationForLabel() {
|
||||||
|
const childObjectUsesLabels = Object.values(this.domainObjectsById || {}).some((object) => object.configuration && object.configuration.useConditionSetOutputAsLabel);
|
||||||
|
const domainObjectUsesLabels = this.domainObject && this.domainObject.configuration && this.domainObject.configuration.useConditionSetOutputAsLabel;
|
||||||
|
|
||||||
|
return childObjectUsesLabels || domainObjectUsesLabels;
|
||||||
|
},
|
||||||
getObjectStyles() {
|
getObjectStyles() {
|
||||||
let objectStyles;
|
let objectStyles;
|
||||||
if (this.domainObjectsById) {
|
if (this.domainObjectsById) {
|
||||||
@ -487,13 +500,14 @@ export default {
|
|||||||
|
|
||||||
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
||||||
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
||||||
|
let output = { output: conditionConfiguration.configuration.output };
|
||||||
if (foundStyle) {
|
if (foundStyle) {
|
||||||
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
|
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style, output);
|
||||||
conditionalStyles.push(foundStyle);
|
conditionalStyles.push(foundStyle);
|
||||||
} else {
|
} else {
|
||||||
conditionalStyles.splice(index, 0, {
|
conditionalStyles.splice(index, 0, {
|
||||||
conditionId: conditionConfiguration.id,
|
conditionId: conditionConfiguration.id,
|
||||||
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
|
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, output)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -715,6 +729,12 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
objectStyle.styles.forEach((conditionalStyle, index) => {
|
objectStyle.styles.forEach((conditionalStyle, index) => {
|
||||||
let style = {};
|
let style = {};
|
||||||
|
if (domainObject.configuration.useConditionSetOutputAsLabel) {
|
||||||
|
style.output = conditionalStyle.style.output;
|
||||||
|
} else {
|
||||||
|
style.output = '';
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
|
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
|
||||||
style[key] = conditionalStyle.style[key];
|
style[key] = conditionalStyle.style[key];
|
||||||
});
|
});
|
||||||
@ -730,12 +750,23 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
if (domainObject.configuration.useConditionSetOutputAsLabel !== true) {
|
||||||
|
let objectConditionStyle = JSON.parse(JSON.stringify(objectStyle));
|
||||||
|
objectConditionStyle.styles.forEach((conditionalStyle) => {
|
||||||
|
conditionalStyle.style.output = '';
|
||||||
|
});
|
||||||
|
domainObjectStyles = {
|
||||||
|
...domainObjectStyles,
|
||||||
|
...objectConditionStyle
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
domainObjectStyles = {
|
domainObjectStyles = {
|
||||||
...domainObjectStyles,
|
...domainObjectStyles,
|
||||||
...objectStyle
|
...objectStyle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return domainObjectStyles;
|
return domainObjectStyles;
|
||||||
},
|
},
|
||||||
@ -743,6 +774,17 @@ export default {
|
|||||||
this.selectedConditionId = conditionId;
|
this.selectedConditionId = conditionId;
|
||||||
this.getAndPersistStyles();
|
this.getAndPersistStyles();
|
||||||
},
|
},
|
||||||
|
persistLabelConfiguration() {
|
||||||
|
if (this.domainObjectsById) {
|
||||||
|
Object.values(this.domainObjectsById).forEach((object) => {
|
||||||
|
this.openmct.objects.mutate(object, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getAndPersistStyles();
|
||||||
|
},
|
||||||
persist(domainObject, style) {
|
persist(domainObject, style) {
|
||||||
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||||
},
|
},
|
||||||
@ -863,6 +905,10 @@ export default {
|
|||||||
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
|
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
|
||||||
|
|
||||||
return layoutItemType && layoutItemType !== 'subobject-view';
|
return layoutItemType && layoutItemType !== 'subobject-view';
|
||||||
|
},
|
||||||
|
updateConditionSetOutputLabel(event) {
|
||||||
|
this.useConditionSetOutputAsLabel = event.target.checked === true;
|
||||||
|
this.persistLabelConfiguration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -39,12 +39,15 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__elem {
|
||||||
|
border-bottom: 1px solid $colorInteriorBorder;
|
||||||
|
padding-bottom: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
&__condition-set {
|
&__condition-set {
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
border-bottom: 1px solid $colorInteriorBorder;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding-bottom: $interiorMargin;
|
|
||||||
|
|
||||||
.c-object-label {
|
.c-object-label {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
@ -23,7 +23,6 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
|
|||||||
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
|
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
|
||||||
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
|
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
|
||||||
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
|
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
|
||||||
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
|
|
||||||
import uuid from "uuid";
|
import uuid from "uuid";
|
||||||
|
|
||||||
export default function ConditionPlugin() {
|
export default function ConditionPlugin() {
|
||||||
@ -55,11 +54,8 @@ export default function ConditionPlugin() {
|
|||||||
domainObject.telemetry = {};
|
domainObject.telemetry = {};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openmct.legacyExtension('policies', {
|
let compositionPolicy = new ConditionSetCompositionPolicy(openmct);
|
||||||
category: 'view',
|
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
|
||||||
implementation: ConditionSetViewPolicy
|
|
||||||
});
|
|
||||||
openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
|
|
||||||
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
|
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
|
||||||
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
|
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
|
||||||
openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));
|
openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));
|
||||||
|
@ -133,6 +133,168 @@ describe('the plugin', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('the condition set usage for condition widgets', () => {
|
||||||
|
let conditionWidgetItem;
|
||||||
|
let selection;
|
||||||
|
let component;
|
||||||
|
let styleViewComponentObject;
|
||||||
|
const conditionSetDomainObject = {
|
||||||
|
"configuration": {
|
||||||
|
"conditionTestData": [
|
||||||
|
{
|
||||||
|
"telemetry": "",
|
||||||
|
"metadata": "",
|
||||||
|
"input": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditionCollection": [
|
||||||
|
{
|
||||||
|
"id": "39584410-cbf9-499e-96dc-76f27e69885d",
|
||||||
|
"configuration": {
|
||||||
|
"name": "Unnamed Condition",
|
||||||
|
"output": "Sine > 0",
|
||||||
|
"trigger": "all",
|
||||||
|
"criteria": [
|
||||||
|
{
|
||||||
|
"id": "85fbb2f7-7595-42bd-9767-a932266c5225",
|
||||||
|
"telemetry": {
|
||||||
|
"namespace": "",
|
||||||
|
"key": "be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||||
|
},
|
||||||
|
"operation": "greaterThan",
|
||||||
|
"input": [
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"metadata": "sin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "35400132-63b0-425c-ac30-8197df7d5862",
|
||||||
|
"telemetry": "any",
|
||||||
|
"operation": "enumValueIs",
|
||||||
|
"input": [
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"metadata": "state"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary": "Match if all criteria are met: Sine Wave Generator Sine > 0 and any telemetry State is OFF "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"isDefault": true,
|
||||||
|
"id": "2532d90a-e0d6-4935-b546-3123522da2de",
|
||||||
|
"configuration": {
|
||||||
|
"name": "Default",
|
||||||
|
"output": "Default",
|
||||||
|
"trigger": "all",
|
||||||
|
"criteria": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"composition": [
|
||||||
|
{
|
||||||
|
"namespace": "",
|
||||||
|
"key": "be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"namespace": "",
|
||||||
|
"key": "077ffa67-e78f-4e99-80e0-522ac33a3888"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"telemetry": {
|
||||||
|
},
|
||||||
|
"name": "Condition Set",
|
||||||
|
"type": "conditionSet",
|
||||||
|
"identifier": {
|
||||||
|
"namespace": "",
|
||||||
|
"key": "863012c1-f6ca-4ab0-aed7-fd43d5e4cd12"
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
conditionWidgetItem = {
|
||||||
|
"label": "Condition Widget",
|
||||||
|
"conditionalLabel": "",
|
||||||
|
"configuration": {
|
||||||
|
},
|
||||||
|
"name": "Condition Widget",
|
||||||
|
"type": "conditionWidget",
|
||||||
|
"identifier": {
|
||||||
|
"namespace": "",
|
||||||
|
"key": "c5e636c1-6771-4c9c-b933-8665cab189b3"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
selection = [
|
||||||
|
[{
|
||||||
|
context: {
|
||||||
|
"item": conditionWidgetItem,
|
||||||
|
"supportsMultiSelect": false
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
let viewContainer = document.createElement('div');
|
||||||
|
child.append(viewContainer);
|
||||||
|
component = new Vue({
|
||||||
|
el: viewContainer,
|
||||||
|
components: {
|
||||||
|
StylesView
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct: openmct,
|
||||||
|
selection: selection,
|
||||||
|
stylesManager
|
||||||
|
},
|
||||||
|
template: '<styles-view/>'
|
||||||
|
});
|
||||||
|
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
styleViewComponentObject = component.$root.$children[0];
|
||||||
|
styleViewComponentObject.setEditState(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
component.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not include the output label when the flag is disabled', () => {
|
||||||
|
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
styleViewComponentObject.conditionalStyles = [];
|
||||||
|
styleViewComponentObject.initializeConditionalStyles();
|
||||||
|
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||||
|
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
const hasNoOutput = styleViewComponentObject.domainObject.configuration.objectStyles.styles.every((style) => {
|
||||||
|
return style.style.output === '' || style.style.output === undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(hasNoOutput).toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes the output label when the flag is enabled', () => {
|
||||||
|
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
styleViewComponentObject.conditionalStyles = [];
|
||||||
|
styleViewComponentObject.initializeConditionalStyles();
|
||||||
|
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||||
|
|
||||||
|
styleViewComponentObject.useConditionSetOutputAsLabel = true;
|
||||||
|
styleViewComponentObject.persistLabelConfiguration();
|
||||||
|
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
const outputs = styleViewComponentObject.domainObject.configuration.objectStyles.styles.map((style) => {
|
||||||
|
return style.style.output;
|
||||||
|
});
|
||||||
|
expect(outputs.join(',')).toEqual('Sine > 0,Default');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('the condition set usage for multiple display layout items', () => {
|
describe('the condition set usage for multiple display layout items', () => {
|
||||||
let displayLayoutItem;
|
let displayLayoutItem;
|
||||||
let lineLayoutItem;
|
let lineLayoutItem;
|
||||||
@ -449,6 +611,10 @@ describe('the plugin', function () {
|
|||||||
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
||||||
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
||||||
Object.keys(foundStyle.style).forEach((key) => {
|
Object.keys(foundStyle.style).forEach((key) => {
|
||||||
|
if (key === 'output') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
||||||
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
|
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
:href="url"
|
:href="url"
|
||||||
>
|
>
|
||||||
<div class="c-condition-widget__label">
|
<div class="c-condition-widget__label">
|
||||||
{{ internalDomainObject.label }}
|
{{ internalDomainObject.conditionalLabel || internalDomainObject.label }}
|
||||||
</div>
|
</div>
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
@ -56,7 +56,7 @@ a.c-condition-widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When the widget is in the main view, center it in the space
|
// 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;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -27,12 +27,15 @@ export default function plugin() {
|
|||||||
openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct));
|
openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct));
|
||||||
|
|
||||||
openmct.types.addType('conditionWidget', {
|
openmct.types.addType('conditionWidget', {
|
||||||
|
key: 'conditionWidget',
|
||||||
name: "Condition Widget",
|
name: "Condition Widget",
|
||||||
description: "A button that can be used on its own, or dynamically styled with a Condition Set.",
|
description: "A button that can be used on its own, or dynamically styled with a Condition Set.",
|
||||||
creatable: true,
|
creatable: true,
|
||||||
cssClass: 'icon-condition-widget',
|
cssClass: 'icon-condition-widget',
|
||||||
initialize(domainObject) {
|
initialize(domainObject) {
|
||||||
|
domainObject.configuration = {};
|
||||||
domainObject.label = 'Condition Widget';
|
domainObject.label = 'Condition Widget';
|
||||||
|
domainObject.conditionalLabel = '';
|
||||||
},
|
},
|
||||||
form: [
|
form: [
|
||||||
{
|
{
|
||||||
|
195
src/plugins/conditionWidget/pluginSpec.js
Normal file
195
src/plugins/conditionWidget/pluginSpec.js
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import { createOpenMct, resetApplicationState } from "utils/testing";
|
||||||
|
import ConditionWidgetPlugin from "./plugin";
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
describe('the plugin', function () {
|
||||||
|
const CONDITION_WIDGET_KEY = 'conditionWidget';
|
||||||
|
let objectDef;
|
||||||
|
let element;
|
||||||
|
let child;
|
||||||
|
let openmct;
|
||||||
|
let mockConditionObjectDefinition;
|
||||||
|
let mockConditionObject;
|
||||||
|
let mockConditionObjectPath;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
mockConditionObjectPath = [
|
||||||
|
{
|
||||||
|
name: 'mock folder',
|
||||||
|
type: 'fake-folder',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-folder',
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mock parent folder',
|
||||||
|
type: 'conditionWidget',
|
||||||
|
identifier: {
|
||||||
|
key: 'mock-parent-folder',
|
||||||
|
namespace: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
mockConditionObjectDefinition = {
|
||||||
|
name: 'Condition Widget',
|
||||||
|
key: 'conditionWidget',
|
||||||
|
creatable: true
|
||||||
|
};
|
||||||
|
|
||||||
|
mockConditionObject = {
|
||||||
|
"conditionWidget": {
|
||||||
|
"identifier": {
|
||||||
|
"namespace": "",
|
||||||
|
"key": "condition-widget-object"
|
||||||
|
},
|
||||||
|
"url": "https://nasa.github.io/openmct/",
|
||||||
|
"label": "Foo Widget",
|
||||||
|
"type": "conditionWidget",
|
||||||
|
"composition": []
|
||||||
|
},
|
||||||
|
"telemetry": {
|
||||||
|
"identifier": {
|
||||||
|
"namespace": "",
|
||||||
|
"key": "telemetry-object"
|
||||||
|
},
|
||||||
|
"type": "test-telemetry-object",
|
||||||
|
"name": "Test Telemetry Object",
|
||||||
|
"telemetry": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"key": "name",
|
||||||
|
"name": "Name",
|
||||||
|
"format": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "utc",
|
||||||
|
"name": "Time",
|
||||||
|
"format": "utc",
|
||||||
|
"hints": {
|
||||||
|
"domain": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Some attribute 1",
|
||||||
|
"key": "some-key-1",
|
||||||
|
"hints": {
|
||||||
|
"range": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Some attribute 2",
|
||||||
|
"key": "some-key-2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeSystem = {
|
||||||
|
timeSystemKey: 'utc',
|
||||||
|
bounds: {
|
||||||
|
start: 1597160002854,
|
||||||
|
end: 1597181232854
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
openmct = createOpenMct(timeSystem);
|
||||||
|
openmct.install(new ConditionWidgetPlugin());
|
||||||
|
|
||||||
|
objectDef = openmct.types.get('conditionWidget').definition;
|
||||||
|
|
||||||
|
element = document.createElement('div');
|
||||||
|
element.style.width = '640px';
|
||||||
|
element.style.height = '480px';
|
||||||
|
child = document.createElement('div');
|
||||||
|
child.style.width = '640px';
|
||||||
|
child.style.height = '480px';
|
||||||
|
element.appendChild(child);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defines a conditionWidget object type with the correct key', () => {
|
||||||
|
expect(objectDef.key).toEqual(mockConditionObjectDefinition.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the conditionWidget object', () => {
|
||||||
|
it('is creatable', () => {
|
||||||
|
expect(objectDef.creatable).toEqual(mockConditionObjectDefinition.creatable);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the view', () => {
|
||||||
|
let conditionWidgetView;
|
||||||
|
let testViewObject;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testViewObject = {
|
||||||
|
id: "test-object",
|
||||||
|
identifier: {
|
||||||
|
key: "test-object",
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: "conditionWidget"
|
||||||
|
};
|
||||||
|
|
||||||
|
const applicableViews = openmct.objectViews.get(testViewObject, mockConditionObjectPath);
|
||||||
|
conditionWidgetView = applicableViews.find((viewProvider) => viewProvider.key === 'conditionWidget');
|
||||||
|
let view = conditionWidgetView.view(testViewObject, element);
|
||||||
|
view.show(child, true);
|
||||||
|
|
||||||
|
return Vue.nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides a view', () => {
|
||||||
|
expect(conditionWidgetView).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have a view provider for condition widget objects", () => {
|
||||||
|
const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
|
||||||
|
|
||||||
|
const conditionWidgetViewProvider = applicableViews.find(
|
||||||
|
(viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(applicableViews.length).toEqual(1);
|
||||||
|
expect(conditionWidgetViewProvider).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render a view with a URL and label", async () => {
|
||||||
|
const urlParent = document.createElement('div');
|
||||||
|
const urlChild = document.createElement('div');
|
||||||
|
urlParent.appendChild(urlChild);
|
||||||
|
|
||||||
|
const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
|
||||||
|
|
||||||
|
const conditionWidgetViewProvider = applicableViews.find(
|
||||||
|
(viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
const conditionWidgetView = conditionWidgetViewProvider.view(mockConditionObject[CONDITION_WIDGET_KEY], [mockConditionObject[CONDITION_WIDGET_KEY]]);
|
||||||
|
conditionWidgetView.show(urlChild);
|
||||||
|
|
||||||
|
await Vue.nextTick();
|
||||||
|
|
||||||
|
const domainUrl = mockConditionObject[CONDITION_WIDGET_KEY].url;
|
||||||
|
expect(urlParent.innerHTML).toContain(`<a href="${domainUrl}"`);
|
||||||
|
|
||||||
|
const conditionWidgetRender = urlParent.querySelector('.c-condition-widget');
|
||||||
|
expect(conditionWidgetRender).toBeDefined();
|
||||||
|
expect(conditionWidgetRender.innerHTML).toContain('<div class="c-condition-widget__label">');
|
||||||
|
|
||||||
|
const conditionWidgetLabel = conditionWidgetRender.querySelector('.c-condition-widget__label');
|
||||||
|
expect(conditionWidgetLabel).toBeDefined();
|
||||||
|
const domainLabel = mockConditionObject[CONDITION_WIDGET_KEY].label;
|
||||||
|
expect(conditionWidgetLabel.textContent).toContain(domainLabel);
|
||||||
|
});
|
||||||
|
});
|
@ -1,3 +1,5 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/******************* FRAME */
|
/******************* FRAME */
|
||||||
.c-frame {
|
.c-frame {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -89,7 +91,7 @@
|
|||||||
&:before {
|
&:before {
|
||||||
// Grippy
|
// Grippy
|
||||||
$h: 4px;
|
$h: 4px;
|
||||||
$tbOffset: ($editFrameMovebarH - $h) / 2;
|
$tbOffset: math.div($editFrameMovebarH - $h, 2);
|
||||||
$lrOffset: 25%;
|
$lrOffset: 25%;
|
||||||
@include grippy($editFrameMovebarColorFg);
|
@include grippy($editFrameMovebarColorFg);
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -38,10 +38,14 @@ export default {
|
|||||||
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
|
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
|
||||||
this.initObjectStyles();
|
this.initObjectStyles();
|
||||||
},
|
},
|
||||||
destroyed() {
|
beforeDestroy() {
|
||||||
if (this.stopListeningObjectStyles) {
|
if (this.stopListeningObjectStyles) {
|
||||||
this.stopListeningObjectStyles();
|
this.stopListeningObjectStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.styleRuleManager) {
|
||||||
|
this.styleRuleManager.destroy();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getObjectStyleForItem(objectStyle) {
|
getObjectStyleForItem(objectStyle) {
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
import JSONExporter from '/src/exporters/JSONExporter.js';
|
import JSONExporter from '/src/exporters/JSONExporter.js';
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { saveAs } from 'saveAs';
|
|
||||||
import uuid from "uuid";
|
import uuid from "uuid";
|
||||||
|
|
||||||
export default class ExportAsJSONAction {
|
export default class ExportAsJSONAction {
|
||||||
@ -41,7 +40,7 @@ export default class ExportAsJSONAction {
|
|||||||
this.calls = 0;
|
this.calls = 0;
|
||||||
this.idMap = {};
|
this.idMap = {};
|
||||||
|
|
||||||
this.JSONExportService = new JSONExporter(saveAs);
|
this.JSONExportService = new JSONExporter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@ -68,7 +67,6 @@ export default class ExportAsJSONAction {
|
|||||||
|
|
||||||
this._write(this.root);
|
this._write(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {object} domainObject
|
* @param {object} domainObject
|
||||||
@ -116,6 +114,7 @@ export default class ExportAsJSONAction {
|
|||||||
return _.isEqual(child.identifier, id);
|
return _.isEqual(child.identifier, id);
|
||||||
});
|
});
|
||||||
const copyOfChild = JSON.parse(JSON.stringify(child));
|
const copyOfChild = JSON.parse(JSON.stringify(child));
|
||||||
|
|
||||||
copyOfChild.identifier.key = uuid();
|
copyOfChild.identifier.key = uuid();
|
||||||
const newIdString = this._getId(copyOfChild);
|
const newIdString = this._getId(copyOfChild);
|
||||||
const parentId = this._getId(parent);
|
const parentId = this._getId(parent);
|
||||||
|
@ -1,252 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[
|
|
||||||
"../../src/actions/ExportAsJSONAction",
|
|
||||||
"../../../entanglement/test/DomainObjectFactory",
|
|
||||||
"../../../../src/MCT",
|
|
||||||
'../../../../src/adapter/capabilities/AdapterCapability'
|
|
||||||
],
|
|
||||||
function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
|
|
||||||
|
|
||||||
describe("The export JSON action", function () {
|
|
||||||
|
|
||||||
let context;
|
|
||||||
let action;
|
|
||||||
let exportService;
|
|
||||||
let identifierService;
|
|
||||||
let typeService;
|
|
||||||
let openmct;
|
|
||||||
let policyService;
|
|
||||||
let mockType;
|
|
||||||
let mockObjectProvider;
|
|
||||||
let exportedTree;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
openmct = new MCT();
|
|
||||||
mockObjectProvider = {
|
|
||||||
objects: {},
|
|
||||||
get: function (id) {
|
|
||||||
return Promise.resolve(mockObjectProvider.objects[id.key]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
openmct.objects.addProvider('', mockObjectProvider);
|
|
||||||
|
|
||||||
exportService = jasmine.createSpyObj('exportService',
|
|
||||||
['exportJSON']);
|
|
||||||
identifierService = jasmine.createSpyObj('identifierService',
|
|
||||||
['generate']);
|
|
||||||
policyService = jasmine.createSpyObj('policyService',
|
|
||||||
['allow']);
|
|
||||||
mockType = jasmine.createSpyObj('type', ['hasFeature']);
|
|
||||||
typeService = jasmine.createSpyObj('typeService', [
|
|
||||||
'getType'
|
|
||||||
]);
|
|
||||||
|
|
||||||
mockType.hasFeature.and.callFake(function (feature) {
|
|
||||||
return feature === 'creation';
|
|
||||||
});
|
|
||||||
|
|
||||||
typeService.getType.and.returnValue(mockType);
|
|
||||||
|
|
||||||
context = {};
|
|
||||||
context.domainObject = domainObjectFactory(
|
|
||||||
{
|
|
||||||
name: 'test',
|
|
||||||
id: 'someID',
|
|
||||||
capabilities: {
|
|
||||||
'adapter': {
|
|
||||||
invoke: invokeAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
identifierService.generate.and.returnValue('brandNewId');
|
|
||||||
exportService.exportJSON.and.callFake(function (tree, options) {
|
|
||||||
exportedTree = tree;
|
|
||||||
});
|
|
||||||
policyService.allow.and.callFake(function (capability, type) {
|
|
||||||
return type.hasFeature(capability);
|
|
||||||
});
|
|
||||||
|
|
||||||
action = new ExportAsJSONAction(openmct, exportService, policyService,
|
|
||||||
identifierService, typeService, context);
|
|
||||||
});
|
|
||||||
|
|
||||||
function invokeAdapter() {
|
|
||||||
let newStyleObject = new AdapterCapability(context.domainObject).invoke();
|
|
||||||
|
|
||||||
return newStyleObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
it("initializes happily", function () {
|
|
||||||
expect(action).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
xit("doesn't export non-creatable objects in tree", function () {
|
|
||||||
let nonCreatableType = {
|
|
||||||
hasFeature:
|
|
||||||
function (feature) {
|
|
||||||
return feature !== 'creation';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typeService.getType.and.returnValue(nonCreatableType);
|
|
||||||
|
|
||||||
let parent = domainObjectFactory({
|
|
||||||
name: 'parent',
|
|
||||||
model: {
|
|
||||||
name: 'parent',
|
|
||||||
location: 'ROOT',
|
|
||||||
composition: ['childId']
|
|
||||||
},
|
|
||||||
id: 'parentId',
|
|
||||||
capabilities: {
|
|
||||||
'adapter': {
|
|
||||||
invoke: invokeAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let child = {
|
|
||||||
identifier: {
|
|
||||||
namespace: '',
|
|
||||||
key: 'childId'
|
|
||||||
},
|
|
||||||
name: 'child',
|
|
||||||
location: 'parentId'
|
|
||||||
};
|
|
||||||
context.domainObject = parent;
|
|
||||||
addChild(child);
|
|
||||||
|
|
||||||
action.perform();
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
}).then(function () {
|
|
||||||
expect(Object.keys(action.tree).length).toBe(1);
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(action.tree, "parentId"))
|
|
||||||
.toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
xit("can export self-containing objects", function () {
|
|
||||||
let parent = domainObjectFactory({
|
|
||||||
name: 'parent',
|
|
||||||
model: {
|
|
||||||
name: 'parent',
|
|
||||||
location: 'ROOT',
|
|
||||||
composition: ['infiniteChildId']
|
|
||||||
},
|
|
||||||
id: 'infiniteParentId',
|
|
||||||
capabilities: {
|
|
||||||
'adapter': {
|
|
||||||
invoke: invokeAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let child = {
|
|
||||||
identifier: {
|
|
||||||
namespace: '',
|
|
||||||
key: 'infiniteChildId'
|
|
||||||
},
|
|
||||||
name: 'child',
|
|
||||||
location: 'infiniteParentId',
|
|
||||||
composition: ['infiniteParentId']
|
|
||||||
};
|
|
||||||
addChild(child);
|
|
||||||
|
|
||||||
context.domainObject = parent;
|
|
||||||
|
|
||||||
action.perform();
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
}).then(function () {
|
|
||||||
expect(Object.keys(action.tree).length).toBe(2);
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(action.tree, "infiniteParentId"))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(action.tree, "infiniteChildId"))
|
|
||||||
.toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
xit("exports links to external objects as new objects", function () {
|
|
||||||
let parent = domainObjectFactory({
|
|
||||||
name: 'parent',
|
|
||||||
model: {
|
|
||||||
name: 'parent',
|
|
||||||
composition: ['externalId'],
|
|
||||||
location: 'ROOT'
|
|
||||||
},
|
|
||||||
id: 'parentId',
|
|
||||||
capabilities: {
|
|
||||||
'adapter': {
|
|
||||||
invoke: invokeAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let externalObject = {
|
|
||||||
name: 'external',
|
|
||||||
location: 'outsideOfTree',
|
|
||||||
identifier: {
|
|
||||||
namespace: '',
|
|
||||||
key: 'externalId'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addChild(externalObject);
|
|
||||||
|
|
||||||
context.domainObject = parent;
|
|
||||||
|
|
||||||
return new Promise (function (resolve) {
|
|
||||||
action.perform();
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
}).then(function () {
|
|
||||||
expect(Object.keys(action.tree).length).toBe(2);
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(action.tree, "parentId"))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(action.tree, "brandNewId"))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(action.tree.brandNewId.location).toBe('parentId');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("exports object tree in the correct format", function () {
|
|
||||||
action.perform();
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
setTimeout(resolve, 100);
|
|
||||||
}).then(function () {
|
|
||||||
expect(Object.keys(exportedTree).length).toBe(2);
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(exportedTree, "openmct")).toBeTruthy();
|
|
||||||
expect(Object.prototype.hasOwnProperty.call(exportedTree, "rootId")).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function addChild(object) {
|
|
||||||
mockObjectProvider.objects[object.identifier.key] = object;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
305
src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
Normal file
305
src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
import {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
describe('Export as JSON plugin', () => {
|
||||||
|
const ACTION_KEY = 'export.JSON';
|
||||||
|
|
||||||
|
let openmct;
|
||||||
|
let domainObject;
|
||||||
|
let exportAsJSONAction;
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
|
||||||
|
exportAsJSONAction = openmct.actions.getAction(ACTION_KEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => resetApplicationState(openmct));
|
||||||
|
|
||||||
|
it('Export as JSON action exist', () => {
|
||||||
|
expect(exportAsJSONAction.key).toEqual(ACTION_KEY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction applies to folder', () => {
|
||||||
|
domainObject = {
|
||||||
|
composition: [],
|
||||||
|
location: 'mine',
|
||||||
|
modified: 1640115501237,
|
||||||
|
name: 'Unnamed Folder',
|
||||||
|
persisted: 1640115501237,
|
||||||
|
type: 'folder'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction applies to telemetry.plot.overlay', () => {
|
||||||
|
domainObject = {
|
||||||
|
composition: [],
|
||||||
|
location: 'mine',
|
||||||
|
modified: 1640115501237,
|
||||||
|
name: 'Unnamed Plot',
|
||||||
|
persisted: 1640115501237,
|
||||||
|
type: 'telemetry.plot.overlay'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction applies to telemetry.plot.stacked', () => {
|
||||||
|
domainObject = {
|
||||||
|
composition: [],
|
||||||
|
location: 'mine',
|
||||||
|
modified: 1640115501237,
|
||||||
|
name: 'Unnamed Plot',
|
||||||
|
persisted: 1640115501237,
|
||||||
|
type: 'telemetry.plot.stacked'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction applies does not applies to non-creatable objects', () => {
|
||||||
|
domainObject = {
|
||||||
|
composition: [],
|
||||||
|
location: 'mine',
|
||||||
|
modified: 1640115501237,
|
||||||
|
name: 'Non Editable Folder',
|
||||||
|
persisted: 1640115501237,
|
||||||
|
type: 'noneditable.folder'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction exports object from tree', (done) => {
|
||||||
|
const parent = {
|
||||||
|
composition: [{
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
}],
|
||||||
|
identifier: {
|
||||||
|
key: 'parent',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Parent',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598129176,
|
||||||
|
location: 'mine',
|
||||||
|
persisted: 1503598129176
|
||||||
|
};
|
||||||
|
|
||||||
|
const child = {
|
||||||
|
composition: [],
|
||||||
|
identifier: {
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Child',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598132428,
|
||||||
|
location: 'parent',
|
||||||
|
persisted: 1503598132428
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(openmct.composition, 'get').and.callFake(object => {
|
||||||
|
return {
|
||||||
|
load: () => {
|
||||||
|
if (object.name === 'Parent') {
|
||||||
|
return Promise.resolve([child]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
|
||||||
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).toBeTruthy();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
exportAsJSONAction.invoke([parent]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ExportAsJSONAction skips non-creatable objects from tree', (done) => {
|
||||||
|
const parent = {
|
||||||
|
composition: [{
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
}],
|
||||||
|
identifier: {
|
||||||
|
key: 'parent',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Parent of Non Editable Child Folder',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598129176,
|
||||||
|
location: 'mine',
|
||||||
|
persisted: 1503598129176
|
||||||
|
};
|
||||||
|
|
||||||
|
const child = {
|
||||||
|
composition: [],
|
||||||
|
identifier: {
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Non Editable Child Folder',
|
||||||
|
type: 'noneditable.folder',
|
||||||
|
modified: 1503598132428,
|
||||||
|
location: 'parent',
|
||||||
|
persisted: 1503598132428
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(openmct.composition, 'get').and.callFake(object => {
|
||||||
|
return {
|
||||||
|
load: () => {
|
||||||
|
if (object.identifier.key === 'parent') {
|
||||||
|
return Promise.resolve([child]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
|
||||||
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).not.toBeTruthy();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
exportAsJSONAction.invoke([parent]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can export self-containing objects', (done) => {
|
||||||
|
const parent = {
|
||||||
|
composition: [{
|
||||||
|
key: 'infinteChild',
|
||||||
|
namespace: ''
|
||||||
|
}],
|
||||||
|
identifier: {
|
||||||
|
key: 'infiniteParent',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'parent',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598129176,
|
||||||
|
location: 'mine',
|
||||||
|
persisted: 1503598129176
|
||||||
|
};
|
||||||
|
|
||||||
|
const child = {
|
||||||
|
composition: [{
|
||||||
|
key: 'infiniteParent',
|
||||||
|
namespace: ''
|
||||||
|
}],
|
||||||
|
identifier: {
|
||||||
|
key: 'infinteChild',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'child',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598132428,
|
||||||
|
location: 'infiniteParent',
|
||||||
|
persisted: 1503598132428
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(openmct.composition, 'get').and.callFake(object => {
|
||||||
|
return {
|
||||||
|
load: () => {
|
||||||
|
if (object.name === 'parent') {
|
||||||
|
return Promise.resolve([child]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
|
||||||
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'infiniteParent')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'infinteChild')).toBeTruthy();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
exportAsJSONAction.invoke([parent]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports links to external objects as new objects', function (done) {
|
||||||
|
const parent = {
|
||||||
|
composition: [{
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
}],
|
||||||
|
identifier: {
|
||||||
|
key: 'parent',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Parent',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598129176,
|
||||||
|
location: 'mine',
|
||||||
|
persisted: 1503598129176
|
||||||
|
};
|
||||||
|
|
||||||
|
const child = {
|
||||||
|
composition: [],
|
||||||
|
identifier: {
|
||||||
|
key: 'child',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
name: 'Child',
|
||||||
|
type: 'folder',
|
||||||
|
modified: 1503598132428,
|
||||||
|
location: 'outsideOfTree',
|
||||||
|
persisted: 1503598132428
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(openmct.composition, 'get').and.callFake(object => {
|
||||||
|
return {
|
||||||
|
load: () => {
|
||||||
|
if (object.name === 'Parent') {
|
||||||
|
return Promise.resolve([child]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
|
||||||
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
|
||||||
|
|
||||||
|
// parent and child objects as part of openmct but child with new id/key
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).not.toBeTruthy();
|
||||||
|
expect(Object.keys(completedTree.openmct).length).toBe(2);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
exportAsJSONAction.invoke([parent]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,9 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
@mixin containerGrippy($headerSize, $dir) {
|
@mixin containerGrippy($headerSize, $dir) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
$h: 6px;
|
$h: 6px;
|
||||||
$minorOffset: ($headerSize - $h) / 2;
|
$minorOffset: math.div($headerSize - $h, 2);
|
||||||
$majorOffset: 35%;
|
$majorOffset: 35%;
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/******************************* GRID VIEW */
|
/******************************* GRID VIEW */
|
||||||
.l-grid-view {
|
.l-grid-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -42,7 +44,7 @@
|
|||||||
&__type-icon {
|
&__type-icon {
|
||||||
filter: $colorKeyFilter;
|
filter: $colorKeyFilter;
|
||||||
flex: 0 0 $gridItemMobile;
|
flex: 0 0 $gridItemMobile;
|
||||||
font-size: floor($gridItemMobile / 2);
|
font-size: floor(math.div($gridItemMobile, 2));
|
||||||
margin-right: $interiorMarginLg;
|
margin-right: $interiorMarginLg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +168,7 @@
|
|||||||
|
|
||||||
&__type-icon {
|
&__type-icon {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
font-size: floor($gridItemDesk / 3);
|
font-size: floor(math.div($gridItemDesk, 3));
|
||||||
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
||||||
order: 2;
|
order: 2;
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
|
@ -106,7 +106,7 @@ export default class CreateAction extends PropertiesAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = '#/browse/' + objectPath
|
const url = '#/browse/' + objectPath
|
||||||
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
|
.map(object => object && this.openmct.objects.makeKeyString(object.identifier))
|
||||||
.reverse()
|
.reverse()
|
||||||
.join('/');
|
.join('/');
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ export default class ImportAsJSONAction {
|
|||||||
* @param {object} object
|
* @param {object} object
|
||||||
* @param {object} changes
|
* @param {object} changes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onSave(object, changes) {
|
onSave(object, changes) {
|
||||||
const selectFile = changes.selectFile;
|
const selectFile = changes.selectFile;
|
||||||
const objectTree = selectFile.body;
|
const objectTree = selectFile.body;
|
||||||
|
@ -43,42 +43,42 @@ const DEFAULTS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../src/adapter/bundle',
|
'../../adapter/bundle',
|
||||||
'../example/eventGenerator/bundle',
|
'../../../example/eventGenerator/bundle',
|
||||||
'../example/export/bundle',
|
'../../../example/export/bundle',
|
||||||
'../example/forms/bundle',
|
'../../../example/forms/bundle',
|
||||||
'../example/identity/bundle',
|
'../../../example/identity/bundle',
|
||||||
'../example/mobile/bundle',
|
'../../../example/mobile/bundle',
|
||||||
'../example/msl/bundle',
|
'../../../example/msl/bundle',
|
||||||
'../example/notifications/bundle',
|
'../../../example/notifications/bundle',
|
||||||
'../example/persistence/bundle',
|
'../../../example/persistence/bundle',
|
||||||
'../example/policy/bundle',
|
'../../../example/policy/bundle',
|
||||||
'../example/profiling/bundle',
|
'../../../example/profiling/bundle',
|
||||||
'../example/scratchpad/bundle',
|
'../../../example/scratchpad/bundle',
|
||||||
'../example/styleguide/bundle',
|
'../../../example/styleguide/bundle',
|
||||||
'../platform/commonUI/browse/bundle',
|
'../../../platform/commonUI/browse/bundle',
|
||||||
'../platform/commonUI/dialog/bundle',
|
'../../../platform/commonUI/dialog/bundle',
|
||||||
'../platform/commonUI/edit/bundle',
|
'../../../platform/commonUI/edit/bundle',
|
||||||
'../platform/commonUI/general/bundle',
|
'../../../platform/commonUI/general/bundle',
|
||||||
'../platform/commonUI/inspect/bundle',
|
'../../../platform/commonUI/inspect/bundle',
|
||||||
'../platform/commonUI/mobile/bundle',
|
'../../../platform/commonUI/mobile/bundle',
|
||||||
'../platform/commonUI/notification/bundle',
|
'../../../platform/commonUI/notification/bundle',
|
||||||
'../platform/commonUI/regions/bundle',
|
'../../../platform/commonUI/regions/bundle',
|
||||||
'../platform/containment/bundle',
|
'../../../platform/containment/bundle',
|
||||||
'../platform/core/bundle',
|
'../../../platform/core/bundle',
|
||||||
'../platform/entanglement/bundle',
|
'../../../platform/entanglement/bundle',
|
||||||
'../platform/exporters/bundle',
|
'../../../platform/exporters/bundle',
|
||||||
'../platform/features/static-markup/bundle',
|
'../../../platform/features/static-markup/bundle',
|
||||||
'../platform/framework/bundle',
|
'../../../platform/framework/bundle',
|
||||||
'../platform/framework/src/load/Bundle',
|
'../../../platform/framework/src/load/Bundle',
|
||||||
'../platform/identity/bundle',
|
'../../../platform/identity/bundle',
|
||||||
'../platform/persistence/aggregator/bundle',
|
'../../../platform/persistence/aggregator/bundle',
|
||||||
'../platform/persistence/elastic/bundle',
|
'../../../platform/persistence/elastic/bundle',
|
||||||
'../platform/persistence/queue/bundle',
|
'../../../platform/persistence/queue/bundle',
|
||||||
'../platform/policy/bundle',
|
'../../../platform/policy/bundle',
|
||||||
'../platform/representation/bundle',
|
'../../../platform/representation/bundle',
|
||||||
'../platform/status/bundle',
|
'../../../platform/status/bundle',
|
||||||
'../platform/telemetry/bundle'
|
'../../../platform/telemetry/bundle'
|
||||||
], function () {
|
], function () {
|
||||||
const LEGACY_BUNDLES = Array.from(arguments);
|
const LEGACY_BUNDLES = Array.from(arguments);
|
||||||
|
|
126
src/plugins/legacySupport/plugin.js
Normal file
126
src/plugins/legacySupport/plugin.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@ -140,11 +140,13 @@ export default class MoveAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate(currentParent) {
|
validate(currentParent) {
|
||||||
return (object, data) => {
|
return (data) => {
|
||||||
const parentCandidate = data.value;
|
const parentCandidatePath = data.value;
|
||||||
|
const parentCandidate = parentCandidatePath[0];
|
||||||
|
|
||||||
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||||
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||||
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
|
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||||
|
|
||||||
if (!parentCandidateKeystring || !currentParentKeystring) {
|
if (!parentCandidateKeystring || !currentParentKeystring) {
|
||||||
return false;
|
return false;
|
||||||
@ -163,7 +165,7 @@ export default class MoveAction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
|
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,14 +22,14 @@
|
|||||||
|
|
||||||
import { MY_ITEMS_KEY } from "./createMyItemsIdentifier";
|
import { MY_ITEMS_KEY } from "./createMyItemsIdentifier";
|
||||||
|
|
||||||
function myItemsInterceptor(identifierObject, openmct) {
|
function myItemsInterceptor(openmct, identifierObject, name) {
|
||||||
|
|
||||||
const myItemsModel = {
|
const myItemsModel = {
|
||||||
identifier: identifierObject,
|
identifier: identifierObject,
|
||||||
"name": "My Items",
|
name,
|
||||||
"type": "folder",
|
type: "folder",
|
||||||
"composition": [],
|
composition: [],
|
||||||
"location": "ROOT"
|
location: "ROOT"
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -23,11 +23,17 @@
|
|||||||
import { createMyItemsIdentifier } from "./createMyItemsIdentifier";
|
import { createMyItemsIdentifier } from "./createMyItemsIdentifier";
|
||||||
import myItemsInterceptor from "./myItemsInterceptor";
|
import myItemsInterceptor from "./myItemsInterceptor";
|
||||||
|
|
||||||
export default function MyItemsPlugin(namespace = '') {
|
const MY_ITEMS_DEFAULT_NAME = 'My Items';
|
||||||
|
|
||||||
|
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '', priority = undefined) {
|
||||||
return function install(openmct) {
|
return function install(openmct) {
|
||||||
const identifier = createMyItemsIdentifier(namespace);
|
const identifier = createMyItemsIdentifier(namespace);
|
||||||
|
|
||||||
openmct.objects.addGetInterceptor(myItemsInterceptor(identifier, openmct));
|
if (priority === undefined) {
|
||||||
openmct.objects.addRoot(identifier);
|
priority = openmct.priority.LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
|
||||||
|
openmct.objects.addRoot(identifier, priority);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ import {
|
|||||||
} from './createMyItemsIdentifier';
|
} from './createMyItemsIdentifier';
|
||||||
|
|
||||||
const MISSING_NAME = `Missing: ${MY_ITEMS_KEY}`;
|
const MISSING_NAME = `Missing: ${MY_ITEMS_KEY}`;
|
||||||
|
const DEFAULT_NAME = 'My Items';
|
||||||
|
const FANCY_NAME = 'Fancy Items';
|
||||||
const myItemsIdentifier = createMyItemsIdentifier();
|
const myItemsIdentifier = createMyItemsIdentifier();
|
||||||
|
|
||||||
describe("the plugin", () => {
|
describe("the plugin", () => {
|
||||||
@ -40,9 +42,10 @@ describe("the plugin", () => {
|
|||||||
name: MISSING_NAME
|
name: MISSING_NAME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
describe('with no arguments passed in', () => {
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
openmct = createOpenMct();
|
openmct = createOpenMct();
|
||||||
|
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
|
|
||||||
openmct.on('start', done);
|
openmct.on('start', done);
|
||||||
@ -61,6 +64,7 @@ describe("the plugin", () => {
|
|||||||
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
|
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
expect(myItems.name).toBe(DEFAULT_NAME);
|
||||||
expect(myItems).toBeDefined();
|
expect(myItems).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,4 +93,31 @@ describe("the plugin", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a name argument passed in', () => {
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.install(openmct.plugins.MyItems(FANCY_NAME));
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'isMissing').and.returnValue(true);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
return resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -175,7 +175,7 @@ export default {
|
|||||||
focusEntryId: null,
|
focusEntryId: null,
|
||||||
search: '',
|
search: '',
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
showTime: 0,
|
showTime: this.domainObject.configuration.showTime || 0,
|
||||||
showNav: false,
|
showNav: false,
|
||||||
sidebarCoversEntries: false
|
sidebarCoversEntries: false
|
||||||
};
|
};
|
||||||
@ -239,6 +239,12 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
search() {
|
search() {
|
||||||
this.getSearchResults();
|
this.getSearchResults();
|
||||||
|
},
|
||||||
|
defaultSort() {
|
||||||
|
mutateObject(this.openmct, this.domainObject, 'configuration.defaultSort', this.defaultSort);
|
||||||
|
},
|
||||||
|
showTime() {
|
||||||
|
mutateObject(this.openmct, this.domainObject, 'configuration.showTime', this.showTime);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
|
@ -245,6 +245,7 @@ export default {
|
|||||||
element: this.snapshot.$el,
|
element: this.snapshot.$el,
|
||||||
onDestroy: () => this.snapshot.$destroy(true),
|
onDestroy: () => this.snapshot.$destroy(true),
|
||||||
size: 'large',
|
size: 'large',
|
||||||
|
autoHide: false,
|
||||||
dismissable: true,
|
dismissable: true,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,6 @@ import {
|
|||||||
describe('the plugin', () => {
|
describe('the plugin', () => {
|
||||||
let notificationIndicatorPlugin;
|
let notificationIndicatorPlugin;
|
||||||
let openmct;
|
let openmct;
|
||||||
let indicatorObject;
|
|
||||||
let indicatorElement;
|
let indicatorElement;
|
||||||
let parentElement;
|
let parentElement;
|
||||||
let mockMessages = ['error', 'test', 'notifications'];
|
let mockMessages = ['error', 'test', 'notifications'];
|
||||||
@ -43,9 +42,6 @@ describe('the plugin', () => {
|
|||||||
|
|
||||||
parentElement = document.createElement('div');
|
parentElement = document.createElement('div');
|
||||||
|
|
||||||
indicatorObject = openmct.indicators.indicatorObjects.find(indicator => indicator.key === 'notifications-indicator');
|
|
||||||
indicatorElement = indicatorObject.element;
|
|
||||||
|
|
||||||
openmct.on('start', () => {
|
openmct.on('start', () => {
|
||||||
mockMessages.forEach(message => {
|
mockMessages.forEach(message => {
|
||||||
openmct.notifications.error(message);
|
openmct.notifications.error(message);
|
||||||
@ -53,7 +49,7 @@ describe('the plugin', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
openmct.startHeadless();
|
openmct.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -68,7 +64,7 @@ describe('the plugin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('notifies the user of the number of notifications', () => {
|
it('notifies the user of the number of notifications', () => {
|
||||||
let notificationCountElement = parentElement.querySelector('.c-indicator__count');
|
let notificationCountElement = document.querySelector('.c-indicator__count');
|
||||||
|
|
||||||
expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
|
expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
|
||||||
});
|
});
|
||||||
|
@ -34,7 +34,7 @@ export default function () {
|
|||||||
name: "Overlay Plot",
|
name: "Overlay Plot",
|
||||||
cssClass: "icon-plot-overlay",
|
cssClass: "icon-plot-overlay",
|
||||||
description: "Combine multiple telemetry elements and view them together as a plot with common X and Y axes. Can be added to Display Layouts.",
|
description: "Combine multiple telemetry elements and view them together as a plot with common X and Y axes. Can be added to Display Layouts.",
|
||||||
creatable: "true",
|
creatable: true,
|
||||||
initialize: function (domainObject) {
|
initialize: function (domainObject) {
|
||||||
domainObject.composition = [];
|
domainObject.composition = [];
|
||||||
domainObject.configuration = {
|
domainObject.configuration = {
|
||||||
|
@ -533,13 +533,6 @@ describe("the plugin", function () {
|
|||||||
let plotViewComponentObject;
|
let plotViewComponentObject;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const getFunc = openmct.$injector.get;
|
|
||||||
spyOn(openmct.$injector, "get")
|
|
||||||
.withArgs("exportImageService").and.returnValue({
|
|
||||||
exportPNG: () => {},
|
|
||||||
exportJPG: () => {}
|
|
||||||
})
|
|
||||||
.and.callFake(getFunc);
|
|
||||||
|
|
||||||
stackedPlotObject = {
|
stackedPlotObject = {
|
||||||
identifier: {
|
identifier: {
|
||||||
|
@ -74,7 +74,9 @@ define([
|
|||||||
'./clock/plugin',
|
'./clock/plugin',
|
||||||
'./DeviceClassifier/plugin',
|
'./DeviceClassifier/plugin',
|
||||||
'./timer/plugin',
|
'./timer/plugin',
|
||||||
'./localStorage/plugin'
|
'./localStorage/plugin',
|
||||||
|
'./legacySupport/plugin.js',
|
||||||
|
'../adapter/indicators/legacy-indicators-plugin'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
UTCTimeSystem,
|
UTCTimeSystem,
|
||||||
@ -129,7 +131,9 @@ define([
|
|||||||
Clock,
|
Clock,
|
||||||
DeviceClassifier,
|
DeviceClassifier,
|
||||||
Timer,
|
Timer,
|
||||||
LocalStorage
|
LocalStorage,
|
||||||
|
LegacySupportPlugin,
|
||||||
|
LegacyIndicatorsPlugin
|
||||||
) {
|
) {
|
||||||
const bundleMap = {
|
const bundleMap = {
|
||||||
Elasticsearch: 'platform/persistence/elastic'
|
Elasticsearch: 'platform/persistence/elastic'
|
||||||
@ -237,6 +241,8 @@ define([
|
|||||||
plugins.Timer = Timer.default;
|
plugins.Timer = Timer.default;
|
||||||
plugins.DeviceClassifier = DeviceClassifier.default;
|
plugins.DeviceClassifier = DeviceClassifier.default;
|
||||||
plugins.LocalStorage = LocalStorage.default;
|
plugins.LocalStorage = LocalStorage.default;
|
||||||
|
plugins.LegacySupport = LegacySupportPlugin.default;
|
||||||
|
plugins.LegacyIndicators = LegacyIndicatorsPlugin;
|
||||||
|
|
||||||
return plugins;
|
return plugins;
|
||||||
});
|
});
|
||||||
|
@ -29,10 +29,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
|
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
|
||||||
const parentType = parent.getCapability('type');
|
if (parent.type === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) {
|
||||||
const newStyleChild = child.useCapability('adapter');
|
|
||||||
|
|
||||||
if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ define([
|
|||||||
const widgetType = {
|
const widgetType = {
|
||||||
name: 'Summary Widget',
|
name: 'Summary Widget',
|
||||||
description: 'A compact status update for collections of telemetry-producing items',
|
description: 'A compact status update for collections of telemetry-producing items',
|
||||||
creatable: true,
|
|
||||||
cssClass: 'icon-summary-widget',
|
cssClass: 'icon-summary-widget',
|
||||||
initialize: function (domainObject) {
|
initialize: function (domainObject) {
|
||||||
domainObject.composition = [];
|
domainObject.composition = [];
|
||||||
@ -85,16 +84,8 @@ define([
|
|||||||
|
|
||||||
return function install(openmct) {
|
return function install(openmct) {
|
||||||
openmct.types.addType('summary-widget', widgetType);
|
openmct.types.addType('summary-widget', widgetType);
|
||||||
openmct.legacyExtension('policies', {
|
let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct);
|
||||||
category: 'composition',
|
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
|
||||||
implementation: SummaryWidgetsCompositionPolicy,
|
|
||||||
depends: ['openmct']
|
|
||||||
});
|
|
||||||
openmct.legacyExtension('policies', {
|
|
||||||
category: 'view',
|
|
||||||
implementation: SummaryWidgetViewPolicy,
|
|
||||||
depends: ['openmct']
|
|
||||||
});
|
|
||||||
openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct));
|
openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct));
|
||||||
openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
|
openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
|
||||||
openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));
|
openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));
|
||||||
|
@ -80,7 +80,6 @@ define([
|
|||||||
this.addHyperlink(domainObject.url, domainObject.openNewTab);
|
this.addHyperlink(domainObject.url, domainObject.openNewTab);
|
||||||
this.watchForChanges(openmct, domainObject);
|
this.watchForChanges(openmct, domainObject);
|
||||||
|
|
||||||
const id = objectUtils.makeKeyString(this.domainObject.identifier);
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,8 +104,6 @@ define([
|
|||||||
|
|
||||||
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
|
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
|
||||||
|
|
||||||
openmct.$injector.get('objectService')
|
|
||||||
.getObjects([id]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
.c-conductor-axis {
|
.c-conductor-axis {
|
||||||
$h: 18px;
|
$h: 18px;
|
||||||
$tickYPos: ($h / 2) + 12px;
|
$tickYPos: math.div($h, 2) + 12px;
|
||||||
|
|
||||||
@include userSelectNone();
|
@include userSelectNone();
|
||||||
@include bgTicks($c: rgba($colorBodyFg, 0.4));
|
@include bgTicks($c: rgba($colorBodyFg, 0.4));
|
||||||
|
@ -185,6 +185,11 @@
|
|||||||
&__inputs,
|
&__inputs,
|
||||||
&__time-bounds {
|
&__time-bounds {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
.c-toggle-switch {
|
||||||
|
// Used in independent Time Conductor
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__inputs {
|
&__inputs {
|
||||||
|
@ -191,7 +191,7 @@ export default {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroyed() {
|
beforeDestroy() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
if (this.unlisten) {
|
if (this.unlisten) {
|
||||||
this.unlisten();
|
this.unlisten();
|
||||||
|
@ -60,6 +60,8 @@ describe("Timer plugin:", () => {
|
|||||||
timerDefinition = openmct.types.get('timer').definition;
|
timerDefinition = openmct.types.get('timer').definition;
|
||||||
timerDefinition.initialize(timerDomainObject);
|
timerDefinition.initialize(timerDomainObject);
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
|
||||||
|
|
||||||
openmct.on('start', resolve);
|
openmct.on('start', resolve);
|
||||||
openmct.start(appHolder);
|
openmct.start(appHolder);
|
||||||
});
|
});
|
||||||
@ -93,6 +95,8 @@ describe("Timer plugin:", () => {
|
|||||||
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
|
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
|
||||||
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
|
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
|
||||||
|
|
||||||
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
|
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
|
||||||
|
|
||||||
timerObjectPath = [mutableTimerObject];
|
timerObjectPath = [mutableTimerObject];
|
||||||
@ -102,6 +106,10 @@ describe("Timer plugin:", () => {
|
|||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
timerView.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
it("should migrate old object properties to the configuration section", () => {
|
it("should migrate old object properties to the configuration section", () => {
|
||||||
openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
|
openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
|
||||||
expect(timerViewObject.configuration.timerFormat).toBe('short');
|
expect(timerViewObject.configuration.timerFormat).toBe('short');
|
||||||
|
@ -21,14 +21,15 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
|
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
|
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
|
||||||
$mobileListIconSize: 30px;
|
$mobileListIconSize: 30px;
|
||||||
$mobileTitleDescH: 35px;
|
$mobileTitleDescH: 35px;
|
||||||
$mobileOverlayMargin: 20px;
|
$mobileOverlayMargin: 20px;
|
||||||
$mobileMenuIconD: 25px;
|
$mobileMenuIconD: 25px;
|
||||||
$phoneItemH: floor($gridItemMobile / 4);
|
$phoneItemH: floor(math.div($gridItemMobile, 4));
|
||||||
$tabletItemH: floor($gridItemMobile / 3);
|
$tabletItemH: floor(math.div($gridItemMobile, 3));
|
||||||
|
|
||||||
/************************** MOBILE TREE MENU DIMENSIONS */
|
/************************** MOBILE TREE MENU DIMENSIONS */
|
||||||
$mobileTreeItemH: 35px;
|
$mobileTreeItemH: 35px;
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/******************************************************** BUTTONS */
|
/******************************************************** BUTTONS */
|
||||||
// Optionally can include icon in :before via markup
|
// Optionally can include icon in :before via markup
|
||||||
button {
|
button {
|
||||||
@ -785,7 +787,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&--swatched {
|
&--swatched {
|
||||||
padding-bottom: floor($pTB / 2);
|
padding-bottom: floor(math.div($pTB, 2));
|
||||||
width: 2em; // Standardize the width
|
width: 2em; // Standardize the width
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -935,7 +937,7 @@ select {
|
|||||||
|
|
||||||
@mixin sliderTrack($bg: $scrollbarTrackColorBg, $knobH: 12px, $trackH: 3px) {
|
@mixin sliderTrack($bg: $scrollbarTrackColorBg, $knobH: 12px, $trackH: 3px) {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
$breakPointPx: floor(($knobH - $trackH) / 2);
|
$breakPointPx: floor(math.div($knobH - $trackH, 2));
|
||||||
$bp1: $breakPointPx;
|
$bp1: $breakPointPx;
|
||||||
$bp2: $breakPointPx + $trackH;
|
$bp2: $breakPointPx + $trackH;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/******************************************************** RESETS */
|
/******************************************************** RESETS */
|
||||||
*,
|
*,
|
||||||
:before,
|
:before,
|
||||||
@ -226,7 +229,7 @@ body.desktop .has-local-controls {
|
|||||||
@include abs();
|
@include abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-grid {
|
.c-grid .c-grid {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
&__x { @include bgTicks($editUIGridColorFg, 'x'); }
|
&__x { @include bgTicks($editUIGridColorFg, 'x'); }
|
||||||
@ -308,7 +311,7 @@ body.desktop .has-local-controls {
|
|||||||
}
|
}
|
||||||
&.c-tree__item {
|
&.c-tree__item {
|
||||||
$d: $waitSpinnerTreeD;
|
$d: $waitSpinnerTreeD;
|
||||||
$spinnerL: 19 + $d/2;
|
$spinnerL: 19 + math.div($d, 2);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/******************************************************************* MESSAGES */
|
/******************************************************************* MESSAGES */
|
||||||
.w-message-contents {
|
.w-message-contents {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
@ -120,7 +123,7 @@ body.desktop .t-message-list {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alert elements in views
|
// Alert elements in views
|
||||||
mixin sUnSynced() {
|
@mixin sUnSynced {
|
||||||
$c: $colorPausedBg;
|
$c: $colorPausedBg;
|
||||||
border: 1px solid $c;
|
border: 1px solid $c;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/*********************************************************************** CLOCKS AND TIMERS */
|
/*********************************************************************** CLOCKS AND TIMERS */
|
||||||
.c-clock,
|
.c-clock,
|
||||||
.c-timer {
|
.c-timer {
|
||||||
@ -788,7 +791,7 @@ body.desktop {
|
|||||||
//width: $splitterHandleD;
|
//width: $splitterHandleD;
|
||||||
}
|
}
|
||||||
&.flush-right {
|
&.flush-right {
|
||||||
width: ceil($splitterHandleD / 2);
|
width: ceil(math.div($splitterHandleD, 2));
|
||||||
&:after {
|
&:after {
|
||||||
width: $splitterHandleD;
|
width: $splitterHandleD;
|
||||||
left: auto; right: 0;
|
left: auto; right: 0;
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/************************** GLYPHS */
|
/************************** GLYPHS */
|
||||||
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
|
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
|
||||||
&:before {
|
&:before {
|
||||||
@ -202,7 +204,7 @@
|
|||||||
|
|
||||||
@mixin bgCheckerboard($c: $colorBodyFg, $opacity: 0.3, $size: 32px, $imp: false) {
|
@mixin bgCheckerboard($c: $colorBodyFg, $opacity: 0.3, $size: 32px, $imp: false) {
|
||||||
$color: rgba($c, $opacity);
|
$color: rgba($c, $opacity);
|
||||||
$bgPos: floor($size / 2);
|
$bgPos: floor(math.div($size, 2));
|
||||||
|
|
||||||
$impStr: null;
|
$impStr: null;
|
||||||
@if $imp {
|
@if $imp {
|
||||||
@ -288,7 +290,7 @@
|
|||||||
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
|
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
$slopedB: $size/$ratio solid transparent;
|
$slopedB: math.div($size, $ratio) solid transparent;
|
||||||
$straightB: $size solid $color;
|
$straightB: $size solid $color;
|
||||||
@if $dir == "up" {
|
@if $dir == "up" {
|
||||||
border-left: $slopedB;
|
border-left: $slopedB;
|
||||||
@ -780,7 +782,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@mixin sUnsynced() {
|
@mixin sUnsynced {
|
||||||
$c: $colorPausedBg;
|
$c: $colorPausedBg;
|
||||||
border: 1px solid $c;
|
border: 1px solid $c;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
export const COLOR_PALETTE = [
|
export const COLOR_PALETTE = [
|
||||||
[0x00, 0x37, 0xFF],
|
[0x43, 0xB0, 0xFF],
|
||||||
[0xF0, 0x60, 0x00],
|
[0xF0, 0x60, 0x00],
|
||||||
[0x00, 0x70, 0x40],
|
[0x00, 0x70, 0x40],
|
||||||
[0xFB, 0x49, 0x49],
|
[0xFB, 0x49, 0x49],
|
||||||
@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
|
|||||||
[0xFF, 0xA6, 0x3D],
|
[0xFF, 0xA6, 0x3D],
|
||||||
[0x05, 0xA3, 0x00],
|
[0x05, 0xA3, 0x00],
|
||||||
[0xF0, 0x00, 0x6C],
|
[0xF0, 0x00, 0x6C],
|
||||||
[0x77, 0x17, 0x7A],
|
[0xAC, 0x54, 0xAE],
|
||||||
[0x23, 0xA9, 0xDB],
|
[0x23, 0xA9, 0xDB],
|
||||||
[0xFA, 0xF0, 0x6F],
|
[0xC7, 0xBE, 0x52],
|
||||||
[0x4E, 0xF0, 0x48],
|
[0x5A, 0xBD, 0x56],
|
||||||
[0xAD, 0x50, 0x72],
|
[0xAD, 0x50, 0x72],
|
||||||
[0x94, 0x25, 0xEA],
|
[0x94, 0x25, 0xEA],
|
||||||
[0x21, 0x87, 0x82],
|
[0x21, 0x87, 0x82],
|
||||||
[0x8F, 0x6E, 0x47],
|
[0x8F, 0x6E, 0x47],
|
||||||
[0xf0, 0x59, 0xcb],
|
[0xf0, 0x59, 0xcb],
|
||||||
[0x34, 0xB6, 0x7D],
|
[0x34, 0xB6, 0x7D],
|
||||||
[0x6A, 0x36, 0xFF],
|
[0x7F, 0x52, 0xFF],
|
||||||
[0x56, 0xF0, 0xE8],
|
[0x46, 0xC7, 0xC0],
|
||||||
[0xA1, 0x8C, 0x1C],
|
[0xA1, 0x8C, 0x1C],
|
||||||
[0xCB, 0xE1, 0x44],
|
[0x95, 0xB1, 0x26],
|
||||||
[0xFF, 0x84, 0x9E],
|
[0xFF, 0x84, 0x9E],
|
||||||
[0xB7, 0x79, 0xE7],
|
[0xB7, 0x79, 0xE7],
|
||||||
[0x8C, 0xC9, 0xFD],
|
[0x8C, 0xC9, 0xFD],
|
||||||
[0xDB, 0xAA, 0x6E],
|
[0xDB, 0xAA, 0x6E],
|
||||||
[0xB8, 0xDF, 0x97],
|
[0x93, 0xB5, 0x77],
|
||||||
[0xFF, 0xBC, 0xDA],
|
[0xFF, 0xBC, 0xDA],
|
||||||
[0xD3, 0xB6, 0xDE]
|
[0xD3, 0xB6, 0xDE]
|
||||||
];
|
];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="domainObject && domainObject.type === 'time-strip'"
|
<div v-if="supportsIndependentTime"
|
||||||
class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
|
class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
|
||||||
>
|
>
|
||||||
<independent-time-conductor :domain-object="domainObject"
|
<independent-time-conductor :domain-object="domainObject"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div ref="objectViewWrapper"
|
<div ref="objectViewWrapper"
|
||||||
:class="objectViewStyle"
|
class="c-object-view"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -20,6 +20,12 @@ import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
|||||||
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||||
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
|
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
|
||||||
|
|
||||||
|
const SupportedViewTypes = [
|
||||||
|
'plot-stacked',
|
||||||
|
'plot-overlay',
|
||||||
|
'bar-graph.view',
|
||||||
|
'time-strip.view'
|
||||||
|
];
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
IndependentTimeConductor
|
IndependentTimeConductor
|
||||||
@ -65,12 +71,10 @@ export default {
|
|||||||
font() {
|
font() {
|
||||||
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
|
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
|
||||||
},
|
},
|
||||||
objectViewStyle() {
|
supportsIndependentTime() {
|
||||||
if (this.domainObject && this.domainObject.type === 'time-strip') {
|
const viewKey = this.getViewKey();
|
||||||
return 'l-shell__main-object-view';
|
|
||||||
} else {
|
return this.domainObject && SupportedViewTypes.includes(viewKey);
|
||||||
return 'u-contents';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
@ -198,6 +202,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.domainObject && this.domainObject.type === 'conditionWidget' && keys.includes('output')) {
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'conditionalLabel', styleObj.output);
|
||||||
|
} else {
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'conditionalLabel', '');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateView(immediatelySelect) {
|
updateView(immediatelySelect) {
|
||||||
this.clear();
|
this.clear();
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
.c-toggle-switch {
|
.c-toggle-switch {
|
||||||
$d: 12px;
|
$d: 12px;
|
||||||
$m: 2px;
|
$m: 2px;
|
||||||
$br: $d/1.5;
|
$br: math.div($d, 1.5);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
/******************************* MAIN AREA */
|
/******************************* MAIN AREA */
|
||||||
&__main-container {
|
&__main-container {
|
||||||
// Wrapper for main views
|
// Wrapper for main views
|
||||||
//display: flex; NEEDS REGRESSION TESTING!!!
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1 1 auto !important;
|
flex: 1 1 auto !important;
|
||||||
@ -243,12 +242,12 @@
|
|||||||
> * + * {
|
> * + * {
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__main-object-view {
|
> .c-object-view {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__tree {
|
&__tree {
|
||||||
// Tree component within __pane-tree
|
// Tree component within __pane-tree
|
||||||
@ -317,6 +316,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-object-view {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.is-editing {
|
.is-editing {
|
||||||
.l-shell__main-container {
|
.l-shell__main-container {
|
||||||
$m: 3px;
|
$m: 3px;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
.c-tree-and-search {
|
.c-tree-and-search {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -136,7 +138,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-left: ceil($interiorMarginSm / 2);
|
margin-left: ceil(math.div($interiorMarginSm, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@include hover {
|
@include hover {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
/**************************** BASE - MOBILE AND DESKTOP */
|
/**************************** BASE - MOBILE AND DESKTOP */
|
||||||
.l-multipane {
|
.l-multipane {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -234,7 +236,7 @@
|
|||||||
|
|
||||||
> .l-pane__handle {
|
> .l-pane__handle {
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translateX(floor($splitterHandleD / -2)); // Center over the pane edge
|
transform: translateX(floor(math.div($splitterHandleD, -2))); // Center over the pane edge
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="expand-button"] {
|
[class*="expand-button"] {
|
||||||
@ -251,7 +253,7 @@
|
|||||||
|
|
||||||
> .l-pane__handle {
|
> .l-pane__handle {
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translateX(floor($splitterHandleD / 2));
|
transform: translateX(floor(math.div($splitterHandleD, 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="expand-button"] {
|
[class*="expand-button"] {
|
||||||
@ -287,7 +289,7 @@
|
|||||||
padding-top: $m;
|
padding-top: $m;
|
||||||
> .l-pane__handle {
|
> .l-pane__handle {
|
||||||
top: 0;
|
top: 0;
|
||||||
transform: translateY(floor($splitterHandleD / -1));
|
transform: translateY(floor(math.div($splitterHandleD, -1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-pane__collapse-button:before {
|
.l-pane__collapse-button:before {
|
||||||
@ -306,7 +308,7 @@
|
|||||||
&[class*="-after"] {
|
&[class*="-after"] {
|
||||||
> .l-pane__handle {
|
> .l-pane__handle {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
transform: translateY(floor($splitterHandleD / 1));
|
transform: translateY(floor(math.div($splitterHandleD, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
|
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
|
||||||
|
@ -24,8 +24,6 @@ class Ticker {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
this.last = new Date() - 1000;
|
this.last = new Date() - 1000;
|
||||||
|
|
||||||
this.tick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +45,7 @@ class Ticker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to update at exactly the next second
|
// Try to update at exactly the next second
|
||||||
setTimeout(() => {
|
this.timeoutHandle = setTimeout(() => {
|
||||||
this.tick();
|
this.tick();
|
||||||
}, 1000 - millis, true);
|
}, 1000 - millis, true);
|
||||||
}
|
}
|
||||||
@ -62,6 +60,10 @@ class Ticker {
|
|||||||
* @returns {Function} a function to unregister this listener
|
* @returns {Function} a function to unregister this listener
|
||||||
*/
|
*/
|
||||||
listen(callback) {
|
listen(callback) {
|
||||||
|
if (this.callbacks.length === 0) {
|
||||||
|
this.tick();
|
||||||
|
}
|
||||||
|
|
||||||
this.callbacks.push(callback);
|
this.callbacks.push(callback);
|
||||||
|
|
||||||
// Provide immediate feedback
|
// Provide immediate feedback
|
||||||
@ -72,6 +74,10 @@ class Ticker {
|
|||||||
this.callbacks = this.callbacks.filter(function (cb) {
|
this.callbacks = this.callbacks.filter(function (cb) {
|
||||||
return cb !== callback;
|
return cb !== callback;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.callbacks.length === 0) {
|
||||||
|
clearTimeout(this.timeoutHandle);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* Note: Open MCT does not intend to support the entire Typescript ecosystem at this time.
|
||||||
|
* This file is intended to add Intellisense for IDEs like VSCode. For more information
|
||||||
|
* about Typescript, please discuss in https://github.com/nasa/openmct/discussions/4693
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"strict": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,21 @@
|
|||||||
|
/* global __dirname */
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const packageDefinition = require('./package.json');
|
const packageDefinition = require('./package.json');
|
||||||
|
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
|
||||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||||
// TODO: Build Constants w/ git-rev-sync
|
|
||||||
const gitRevision = require('child_process')
|
const gitRevision = require('child_process')
|
||||||
.execSync('git rev-parse HEAD')
|
.execSync('git rev-parse HEAD')
|
||||||
.toString().trim();
|
.toString().trim();
|
||||||
const gitBranch = require('child_process')
|
const gitBranch = require('child_process')
|
||||||
.execSync('git rev-parse --abbrev-ref HEAD')
|
.execSync('git rev-parse --abbrev-ref HEAD')
|
||||||
.toString().trim();
|
.toString().trim();
|
||||||
const vueFile = devMode
|
|
||||||
? path.join(__dirname, "node_modules/vue/dist/vue.js")
|
|
||||||
: path.join(__dirname, "node_modules/vue/dist/vue.min.js");
|
|
||||||
|
|
||||||
const webpackConfig = {
|
/** @type {import('webpack').Configuration} */
|
||||||
mode: devMode ? 'development' : 'production',
|
const config = {
|
||||||
entry: {
|
entry: {
|
||||||
openmct: './openmct.js',
|
openmct: './openmct.js',
|
||||||
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
|
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
|
||||||
@ -33,7 +29,9 @@ const webpackConfig = {
|
|||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
library: '[name]',
|
library: '[name]',
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
path: path.resolve(__dirname, 'dist')
|
publicPath: '',
|
||||||
|
hashFunction: 'xxhash64',
|
||||||
|
clean: true
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@ -45,7 +43,6 @@ const webpackConfig = {
|
|||||||
"bourbon": "bourbon.scss",
|
"bourbon": "bourbon.scss",
|
||||||
"plotly-basic": "plotly.js-basic-dist",
|
"plotly-basic": "plotly.js-basic-dist",
|
||||||
"plotly-gl2d": "plotly.js-gl2d-dist",
|
"plotly-gl2d": "plotly.js-gl2d-dist",
|
||||||
"vue": vueFile,
|
|
||||||
"d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
|
"d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
|
||||||
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
|
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
|
||||||
"styles": path.join(__dirname, "src/styles"),
|
"styles": path.join(__dirname, "src/styles"),
|
||||||
@ -55,21 +52,16 @@ const webpackConfig = {
|
|||||||
"utils": path.join(__dirname, "src/utils")
|
"utils": path.join(__dirname, "src/utils")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
devtool: devMode ? 'eval-source-map' : 'source-map',
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__OPENMCT_VERSION__: `'${packageDefinition.version}'`,
|
__OPENMCT_VERSION__: `'${packageDefinition.version}'`,
|
||||||
__OPENMCT_BUILD_DATE__: `'${new Date()}'`,
|
__OPENMCT_BUILD_DATE__: `'${new Date()}'`,
|
||||||
__OPENMCT_REVISION__: `'${gitRevision}'`,
|
__OPENMCT_REVISION__: `'${gitRevision}'`,
|
||||||
__OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`,
|
__OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`
|
||||||
__OPENMCT_ROOT_RELATIVE__: `'${devMode ? 'dist/' : ''}'`
|
|
||||||
}),
|
}),
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
new MiniCssExtractPlugin({
|
new CopyWebpackPlugin({
|
||||||
filename: '[name].css',
|
patterns: [
|
||||||
chunkFilename: '[name].css'
|
|
||||||
}),
|
|
||||||
new CopyWebpackPlugin([
|
|
||||||
{
|
{
|
||||||
from: 'src/images/favicons',
|
from: 'src/images/favicons',
|
||||||
to: 'favicons'
|
to: 'favicons'
|
||||||
@ -80,7 +72,12 @@ const webpackConfig = {
|
|||||||
return content.toString().replace(/dist\//g, '');
|
return content.toString().replace(/dist\//g, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: '[name].css',
|
||||||
|
chunkFilename: '[name].css'
|
||||||
|
})
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@ -88,10 +85,17 @@ const webpackConfig = {
|
|||||||
test: /\.(sc|sa|c)ss$/,
|
test: /\.(sc|sa|c)ss$/,
|
||||||
use: [
|
use: [
|
||||||
MiniCssExtractPlugin.loader,
|
MiniCssExtractPlugin.loader,
|
||||||
'css-loader',
|
{
|
||||||
'fast-sass-loader'
|
loader: 'css-loader'
|
||||||
|
},
|
||||||
|
'resolve-url-loader',
|
||||||
|
'sass-loader'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
use: 'vue-loader'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.html$/,
|
||||||
use: 'html-loader'
|
use: 'html-loader'
|
||||||
@ -104,7 +108,7 @@ const webpackConfig = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(jpg|jpeg|png|svg|ico|woff2?|eot|ttf)$/,
|
test: /\.(jpg|jpeg|png|svg|ico|woff|woff2?|eot|ttf)$/,
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[ext]',
|
name: '[name].[ext]',
|
||||||
@ -117,26 +121,17 @@ const webpackConfig = {
|
|||||||
return `icons/${url}`;
|
return `icons/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/\.(woff2?|eot|ttf)$/.test(url)) {
|
if (/\.(woff|woff2?|eot|ttf)$/.test(url)) {
|
||||||
return `fonts/${url}`;
|
return `fonts/${url}`;
|
||||||
} else {
|
} else {
|
||||||
return `${url}`;
|
return `${url}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.vue$/,
|
|
||||||
use: 'vue-loader'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
stats: {
|
stats: 'errors-warnings'
|
||||||
modules: false,
|
|
||||||
timings: true,
|
|
||||||
colors: true,
|
|
||||||
warningsFilter: /asset size limit/g
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = webpackConfig;
|
module.exports = config;
|
20
webpack.dev.js
Normal file
20
webpack.dev.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common');
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'development',
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"vue": path.join(__dirname, "node_modules/vue/dist/vue.js")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
__OPENMCT_ROOT_RELATIVE__: '"dist/"'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
devtool: 'eval-source-map'
|
||||||
|
});
|
20
webpack.prod.js
Normal file
20
webpack.prod.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common');
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"vue": path.join(__dirname, "node_modules/vue/dist/vue.min.js")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
__OPENMCT_ROOT_RELATIVE__: '""'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
devtool: 'source-map'
|
||||||
|
});
|
Reference in New Issue
Block a user