Compare commits

..

3 Commits

Author SHA1 Message Date
John Hill
ab93ca9024 Merge branch 'master' into sprint-1.7.3 2021-05-24 10:48:53 -07:00
Shefali Joshi
80451935a9 Merge branch 'master' into sprint-1.7.3 2021-05-21 13:22:17 -07:00
Joshi
783e0fd4f8 Prepare snapshot for sprint 1.7.3 2021-05-17 11:29:58 -07:00
92 changed files with 1834 additions and 2157 deletions

View File

@@ -1,43 +0,0 @@
<!--- This is for filing bugs. If you have a general question, please -->
<!--- visit https://github.com/nasa/openmct/discussions -->
---
name: Bug Report
about: File a Bug !
---
<!--- Focus on user impact in the title. Use the Summary Field to -->
<!--- describe the problem technically. -->
#### Summary
<!--- A description of the issue encountered. When possible, a description -->
<!--- of the impact of the issue. What use case does it impede?-->
#### Expected vs Current Behavior
<!--- Tell us what should have happened -->
#### Impact Check List
<!--- Please select from the following options -->
- [ ] Data loss or misrepresented data?
- [ ] Regression? Did this used to work or has it always been broken?
- [ ] Is there a workaround available?
- [ ] Does this impact a critical component?
- [ ] Is this just a visual bug?
#### Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
#### Environment
* Open MCT Version: <!--- date of build, version, or SHA -->
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yams? -->
* OS:
* Browser:
#### Additional Information
<!--- Include any screenshots, gifs, or logs which will expedite triage -->

View File

@@ -1 +0,0 @@
blank_issues_enabled: false

View File

@@ -1,23 +0,0 @@
<!--- This is for filing enhancements or features. If you have a general -->
<!--- question, please visit https://github.com/nasa/openmct/discussions -->
---
name: Feature Request
about: Suggest an idea for this project
---
<!--
Thank you for suggesting an idea to make Open MCT better.
Please fill in as much of the template below as you're able.
-->
**Is your feature request related to a problem? Please describe.**
<!-- Please describe the problem you are trying to solve. -->
**Describe the solution you'd like**
<!--- Please describe the desired behavior. -->
**Describe alternatives you've considered**
<!--- Please describe alternative solutions or features you have considered. -->

View File

@@ -1,12 +0,0 @@
### All Submissions:
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/nasa/openmct/pulls) for the same update/change?
### Author Checklist
* [ ] Changes address original issue?
* [ ] Unit tests included and/or updated with changes?
* [ ] Command line build passes?
* [ ] Has this been smoke tested?
* [ ] Testing instructions included in associated issue?

View File

@@ -10,7 +10,7 @@ accept changes from external contributors.
The short version:
1. Write your contribution or describe your idea in the form of an [GitHub issue](https://github.com/nasa/openmct/issues/new/choose) or [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions)
1. Write your contribution.
2. Make sure your contribution meets code, test, and commit message
standards as described below.
3. Submit a pull request from a topic branch back to `master`. Include a check
@@ -18,7 +18,6 @@ The short version:
for review.)
4. Respond to any discussion. When the reviewer decides it's ready, they
will merge back `master` and fill out their own check list.
5. If you are a first-time contributor, please see [this discussion](https://github.com/nasa/openmct/discussions/3821) for further information.
## Contribution Process
@@ -116,7 +115,7 @@ the pull request containing the reviewer checklist (from below) and complete
the merge back to the master branch.
Additionally:
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull requests __author__. If no issue exists, [create one](https://github.com/nasa/openmct/issues/new/choose).
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull requests __author__. If no issue exists, create one.
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull requests __author__.
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
@@ -297,12 +296,23 @@ these standards.
Issues are tracked at https://github.com/nasa/openmct/issues.
Issues should include:
* A short description of the issue encountered.
* A longer-form description of the issue encountered. When possible, steps to
reproduce the issue.
* When possible, a description of the impact of the issue. What use case does
it impede?
* An assessment of the severity of the issue.
Issue severity is categorized as follows (in ascending order):
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
* _Medium_: Some impairment of use, but simple workarounds exist
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
* _Trivial_: Minimal impact on the usefulness and functionality of the
software; a "nice-to-have."
* _(Unspecified)_: Major loss of functionality or impairment of use.
* _Critical_: Large-scale loss of functionality or impairment of use,
such that remaining utility becomes marginal.
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
## Check Lists
@@ -312,19 +322,16 @@ checklist).
### Author Checklist
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
1. Changes address original issue?
2. Unit tests included and/or updated with changes?
3. Command line build passes?
4. Changes have been smoke-tested?
5. Testing instructions included?
### Reviewer Checklist
* [ ] Changes appear to address issue?
* [ ] Appropriate unit tests included?
* [ ] Code style and in-line documentation are appropriate?
* [ ] Commit messages meet standards?
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
* [ ] List of Acceptance Tests Performed.
Write out a small list of tests performed with just enough detail for another developer on the team
to execute.
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```
1. Changes appear to address issue?
2. Appropriate unit tests included?
3. Code style and in-line documentation are appropriate?
4. Commit messages meet standards?
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)

View File

@@ -44,7 +44,7 @@ The clearest examples for developing Open MCT plugins are in the
our documentation.
We want Open MCT to be as easy to use, install, run, and develop for as
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues/new/choose), [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
## Building Applications With Open MCT

View File

@@ -73,11 +73,11 @@ acceptance testing (e.g. by resolving any blockers found); any
resources not needed for this effort should be used to begin work
for the subsequent sprint.
| Week | Mon | Tue | Wed | Thu | Fri |
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
| __1__ | Sprint plan | Tag-up | | | |
| __2__ | | Tag-up | | | Code freeze and sprint branch |
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
| Week | Mon | Tue | Wed | Thu | Fri |
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
| __1__ | Sprint plan | Tag-up | | | |
| __2__ | | Tag-up | | | Code freeze |
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship |
&ast; If necessary.
@@ -105,20 +105,14 @@ emphasis on testing.
that team may begin work for that sprint during the
third week, since testing and blocker resolution is unlikely
to require all available resources.
* Testing success criteria identified per issue (where necessary). This could be in the form of acceptance tests on the issue or detailing performance tests, for example.
* __Tag-up.__ Check in and status update among development team.
May amend plan for sprint as-needed.
* __Code freeze.__ Any new work from this sprint
(features, bug fixes, enhancements) must be integrated by the
end of the second week of the sprint. After code freeze, a sprint
branch will be created (and until the end of the sprint) the only
changes that should be merged into the sprint branch should
directly address issues needed to pass acceptance testing.
During this time, any other feature development will continue to
be merged into the master branch for the next sprint.
* __Sprint branch merge to master.__ After acceptance testing, the sprint branch
will be merged back to the master branch. Any code conflicts that
arise will be resolved by the team.
end of the second week of the sprint. After code freeze
(and until the end of the sprint) the only changes that should be
merged into the master branch should directly address issues
needed to pass acceptance testing.
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
Structured testing with predefined
success criteria. No release should ship without passing
@@ -132,8 +126,8 @@ emphasis on testing.
* [__Testathon.__](testing/plan.md#user-testing)
Multi-user testing, involving as many users as
is feasible, plus development team. Open-ended; should verify
completed work from this sprint using the sprint branch, test
exploratorily for regressions, et cetera.
completed work from this sprint, test exploratorily for
regressions, et cetera.
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
test to verify that the software remains
stable after running for longer durations. May include some
@@ -149,7 +143,7 @@ emphasis on testing.
Subset of Pre-release Testing
which should be performed before shipping at the end of any
sprint. Time is allocated for a second round of
Pre-release Testing if the first round is not passed. Smoke tests collected from issues/PRs
Pre-release Testing if the first round is not passed.
* __Triage.__ Team reviews issues from acceptance testing and uses
success criteria to determine whether or not they should block
release, then formulates a plan to address these issues before

View File

@@ -19,7 +19,7 @@ Testing for Open MCT includes:
Manual, non-rigorous testing of the software and/or specific features
of interest. Verifies that the software runs and that basic functionality
is present. The outcome of Smoke Testing should be a simplified list of Acceptance Tests which could be executed by another team member with sufficient context.
is present.
### Unit Testing
@@ -49,7 +49,7 @@ User testing will focus on the following activities:
* General "trying to break things."
During user testing, users will
[report issues](https://github.com/nasa/openmct/issues/new/choose)
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
as they are encountered.
Desired outcomes of user testing are:
@@ -71,7 +71,7 @@ usage. After twenty-four hours, the software is evaluated for:
at the start of the test? Is it as responsive?
Any defects or unexpected behavior identified during testing should be
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
and reviewed for severity.
## Test Performance

View File

@@ -92,8 +92,8 @@ should update (or delegate the task of updating) Open MCT version
numbers by the following process:
1. Update version number in `package.json`
1. Checkout branch created for the last sprint that has been successfully tested.
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
1. Create a new branch off the `master` branch.
2. Remove `-SNAPSHOT` suffix from the version in `package.json`.
3. Verify that resulting version number meets semantic versioning
requirements relative to previous stable version. Increment the
version number if necessary.

View File

@@ -93,36 +93,5 @@ define([
};
};
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
return {
limits: function () {
return {
WARNING: {
low: {
cssClass: "is-limit--lwr is-limit--yellow",
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
cssClass: "is-limit--upr is-limit--yellow",
...YELLOW
}
},
DISTRESS: {
low: {
cssClass: "is-limit--lwr is-limit--red",
sin: -RED.sin,
cos: -RED.cos
},
high: {
cssClass: "is-limit--upr is-limit--red",
...RED
}
}
};
}
};
};
return SinewaveLimitProvider;
});

View File

@@ -78,7 +78,6 @@ module.exports = (config) => {
preserveDescribeNesting: true,
foldAll: false
},
browserConsoleLogOptions: { level: "error", format: "%b %T: %m", terminal: true },
coverageIstanbulReporter: {
fixWebpackSourcePaths: true,
dir: process.env.CIRCLE_ARTIFACTS ?

View File

@@ -78,8 +78,7 @@
"zepto": "^1.2.0"
},
"scripts": {
"clean": "rm -rf ./dist /node_modules; rm package-lock.json",
"clean-test-lint": "npm run clean; npm install ; npm run test; npm run lint",
"clean": "rm -rf ./dist",
"start": "node app.js",
"lint": "eslint platform example src --ext .js,.vue openmct.js",
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",

View File

@@ -86,7 +86,7 @@ define(
})
.join('/');
openmct.router.navigate(url);
window.location.href = url;
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
openmct.editor.edit();

View File

@@ -23,11 +23,13 @@
define([
"moment-timezone",
"./src/indicators/ClockIndicator",
"./src/indicators/FollowIndicator",
"./src/services/TickerService",
"./src/services/TimerService",
"./src/controllers/ClockController",
"./src/controllers/TimerController",
"./src/controllers/RefreshingController",
"./src/actions/FollowTimerAction",
"./src/actions/StartTimerAction",
"./src/actions/RestartTimerAction",
"./src/actions/StopTimerAction",
@@ -37,11 +39,13 @@ define([
], function (
MomentTimezone,
ClockIndicator,
FollowIndicator,
TickerService,
TimerService,
ClockController,
TimerController,
RefreshingController,
FollowTimerAction,
StartTimerAction,
RestartTimerAction,
StopTimerAction,
@@ -140,6 +144,15 @@ define([
}
],
"actions": [
{
"key": "timer.follow",
"implementation": FollowTimerAction,
"depends": ["timerService"],
"category": "contextual",
"name": "Follow Timer",
"cssClass": "icon-clock",
"priority": "optional"
},
{
"key": "timer.start",
"implementation": StartTimerAction,
@@ -286,7 +299,10 @@ define([
}
}
],
"runs": [],
"runs": [{
"implementation": FollowIndicator,
"depends": ["openmct", "timerService"]
}],
"licenses": [
{
"name": "moment-duration-format",

View File

@@ -0,0 +1,56 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, 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(
[],
function () {
/**
* Designates a specific timer for following. Timelines, for example,
* use the actively followed timer to display a time-of-interest line
* and interpret time conductor bounds in the Timeline's relative
* time frame.
*
* @implements {Action}
* @memberof platform/features/clock
* @constructor
* @param {ActionContext} context the context for this action
*/
function FollowTimerAction(timerService, context) {
var domainObject =
context.domainObject
&& context.domainObject.useCapability('adapter');
this.perform =
timerService.setTimer.bind(timerService, domainObject);
}
FollowTimerAction.appliesTo = function (context) {
var model =
(context.domainObject && context.domainObject.getModel())
|| {};
return model.type === 'timer';
};
return FollowTimerAction;
}
);

View File

@@ -0,0 +1,51 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2018, 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([], function () {
/**
* Indicator that displays the active timer, as well as its
* current state.
* @memberof platform/features/clock
*/
return function installFollowIndicator(openmct, timerService) {
var indicator = openmct.indicators.simpleIndicator();
var timer = timerService.getTimer();
setIndicatorStatus(timer);
function setIndicatorStatus(newTimer) {
if (newTimer !== undefined) {
indicator.iconClass('icon-timer');
indicator.statusClass('s-status-on');
indicator.text('Following timer ' + newTimer.name);
} else {
indicator.iconClass('icon-timer');
indicator.statusClass('s-status-disabled');
indicator.text('No timer being followed');
}
}
timerService.on('change', setIndicatorStatus);
openmct.indicators.add(indicator);
};
});

View File

@@ -0,0 +1,89 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, 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/FollowTimerAction"
], function (FollowTimerAction) {
var TIMER_SERVICE_METHODS =
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
describe("The Follow Timer action", function () {
var testContext;
var testModel;
var testAdaptedObject;
beforeEach(function () {
testModel = {};
testContext = {
domainObject: jasmine.createSpyObj('domainObject', [
'getModel',
'useCapability'
])
};
testAdaptedObject = { foo: 'bar' };
testContext.domainObject.getModel.and.returnValue(testModel);
testContext.domainObject.useCapability.and.callFake(function (c) {
return c === 'adapter' && testAdaptedObject;
});
});
it("is applicable to timers", function () {
testModel.type = "timer";
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
});
it("is inapplicable to non-timers", function () {
testModel.type = "folder";
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
});
describe("when instantiated", function () {
var mockTimerService;
var action;
beforeEach(function () {
mockTimerService = jasmine.createSpyObj(
'timerService',
TIMER_SERVICE_METHODS
);
action = new FollowTimerAction(mockTimerService, testContext);
});
it("does not interact with the timer service", function () {
TIMER_SERVICE_METHODS.forEach(function (method) {
expect(mockTimerService[method]).not.toHaveBeenCalled();
});
});
describe("and performed", function () {
beforeEach(function () {
action.perform();
});
it("sets the active timer", function () {
expect(mockTimerService.setTimer)
.toHaveBeenCalledWith(testAdaptedObject);
});
});
});
});
});

View File

@@ -0,0 +1,96 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2018, 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/indicators/FollowIndicator",
"../../src/services/TimerService",
"../../../../../src/MCT",
'zepto'
], function (
FollowIndicator,
TimerService,
MCT,
$
) {
xdescribe("The timer-following indicator", function () {
var timerService;
var openmct;
beforeEach(function () {
openmct = new MCT();
timerService = new TimerService(openmct);
spyOn(openmct.indicators, "add");
});
it("adds an indicator when installed", function () {
FollowIndicator(openmct, timerService);
expect(openmct.indicators.add).toHaveBeenCalled();
});
it("indicates that no timer is being followed", function () {
FollowIndicator(openmct, timerService);
var simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
var element = simpleIndicator.element;
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('No timer being followed');
});
describe("when a timer is set", function () {
var testObject;
var simpleIndicator;
beforeEach(function () {
testObject = {
identifier: {
namespace: 'namespace',
key: 'key'
},
name: "some timer!"
};
timerService.setTimer(testObject);
FollowIndicator(openmct, timerService);
simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
});
it("displays the timer's name", function () {
var element = simpleIndicator.element;
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('Following timer ' + testObject.name);
});
it("displays the timer's name when it changes", function () {
var secondTimer = {
identifier: {
namespace: 'namespace',
key: 'key2'
},
name: "Some other timer"
};
var element = simpleIndicator.element;
timerService.setTimer(secondTimer);
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('Following timer ' + secondTimer.name);
});
});
});
});

View File

@@ -252,7 +252,7 @@ define([
this.status = new api.StatusAPI(this);
this.router = new ApplicationRouter(this);
this.router = new ApplicationRouter();
this.branding = BrandingAPI.default;

View File

@@ -161,22 +161,6 @@ define([
evaluate: function (datum, property) {
return limitEvaluator.evaluate(datum, property && property.key);
}
};
};
LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
const oldObject = this.instantiate(
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier)
);
const limitEvaluator = oldObject.getCapability("limit");
return {
limits: function () {
return limitEvaluator.limits();
}
};
};

View File

@@ -119,8 +119,7 @@ describe('The ActionCollection', () => {
afterEach(() => {
actionCollection.destroy();
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
describe("disable method invoked with action keys", () => {

View File

@@ -99,7 +99,7 @@ describe('The Actions API', () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
describe("register method", () => {

View File

@@ -215,12 +215,12 @@ define([
* @memberof {module:openmct.CompositionCollection#}
* @name load
*/
CompositionCollection.prototype.load = function (abortSignal) {
CompositionCollection.prototype.load = function () {
this.cleanUpMutables();
return this.provider.load(this.domainObject)
.then(function (children) {
return Promise.all(children.map((c) => this.publicAPI.objects.get(c, abortSignal)));
return Promise.all(children.map((c) => this.publicAPI.objects.get(c)));
}.bind(this))
.then(function (childObjects) {
childObjects.forEach(c => this.add(c, true));

View File

@@ -76,7 +76,7 @@ describe ('The Menu API', () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
describe("showMenu method", () => {

View File

@@ -22,7 +22,7 @@ describe("The Status API", () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
describe("set function", () => {

View File

@@ -504,26 +504,6 @@ define([
return this.getLimitEvaluator(domainObject);
};
/**
* Get a limits for this domain object.
* Limits help you display limits and alarms of
* telemetry for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
* that no limits are defined for this domain object's telemetry.
*
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to get limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method limits
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.limitDefinition = function (domainObject) {
return this.getLimits(domainObject);
};
/**
* Get a limit evaluator for this domain object.
* Limit Evaluators help you evaluate limit and alarm status of individual
@@ -551,42 +531,5 @@ define([
return provider.getLimitEvaluator(domainObject);
};
/**
* Get a limit definitions for this domain object.
* Limit Definitions help you indicate limits and alarms of
* telemetry for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
* that no limits are defined for this domain object's telemetry.
*
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to display limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method limits returns a limits object of
* type {
* level1: {
* low: { key1: value1, key2: value2 },
* high: { key1: value1, key2: value2 }
* },
* level2: {
* low: { key1: value1, key2: value2 },
* high: { key1: value1, key2: value2 }
* }
* }
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.getLimits = function (domainObject) {
const provider = this.findLimitEvaluator(domainObject);
if (!provider || !provider.getLimits) {
return {
limits: function () {}
};
}
return provider.getLimits(domainObject);
};
return TelemetryAPI;
});

View File

@@ -43,15 +43,15 @@ export default function LADTableSetViewProvider(openmct) {
components: {
LadTableSet: LadTableSet
},
provide: {
openmct,
objectPath
},
data() {
return {
domainObject
};
},
provide: {
openmct,
objectPath
},
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
});
},

View File

@@ -292,11 +292,6 @@ describe("The LAD Table Set", () => {
});
afterEach(() => {
openmct.time.timeSystem('utc', {
start: 0,
end: 1
});
return resetApplicationState(openmct);
});

View File

@@ -19,6 +19,10 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
getAllSearchParams,
setAllSearchParams
} from 'utils/openmctLocation';
const TIME_EVENTS = ['timeSystem', 'clock', 'clockOffsets'];
const SEARCH_MODE = 'tc.mode';
@@ -45,8 +49,9 @@ export default class URLTimeSettingsSynchronizer {
}
initialize() {
this.openmct.router.on('change:params', this.updateTimeSettings);
this.updateTimeSettings();
window.addEventListener('hashchange', this.updateTimeSettings);
TIME_EVENTS.forEach(event => {
this.openmct.time.on(event, this.setUrlFromTimeApi);
});
@@ -54,8 +59,7 @@ export default class URLTimeSettingsSynchronizer {
}
destroy() {
this.openmct.router.off('change:params', this.updateTimeSettings);
window.removeEventListener('hashchange', this.updateTimeSettings);
this.openmct.off('start', this.initialize);
this.openmct.off('destroy', this.destroy);
@@ -66,18 +70,22 @@ export default class URLTimeSettingsSynchronizer {
}
updateTimeSettings() {
let timeParameters = this.parseParametersFromUrl();
// Prevent from triggering self
if (!this.isUrlUpdateInProgress) {
let timeParameters = this.parseParametersFromUrl();
if (this.areTimeParametersValid(timeParameters)) {
this.setTimeApiFromUrl(timeParameters);
this.openmct.router.setLocationFromUrl();
if (this.areTimeParametersValid(timeParameters)) {
this.setTimeApiFromUrl(timeParameters);
} else {
this.setUrlFromTimeApi();
}
} else {
this.setUrlFromTimeApi();
this.isUrlUpdateInProgress = false;
}
}
parseParametersFromUrl() {
let searchParams = this.openmct.router.getAllSearchParams();
let searchParams = getAllSearchParams();
let mode = searchParams.get(SEARCH_MODE);
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
@@ -140,7 +148,7 @@ export default class URLTimeSettingsSynchronizer {
}
setUrlFromTimeApi() {
let searchParams = this.openmct.router.getAllSearchParams();
let searchParams = getAllSearchParams();
let clock = this.openmct.time.clock();
let bounds = this.openmct.time.bounds();
let clockOffsets = this.openmct.time.clockOffsets();
@@ -168,7 +176,8 @@ export default class URLTimeSettingsSynchronizer {
}
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
this.openmct.router.setAllSearchParams(searchParams);
this.isUrlUpdateInProgress = true;
setAllSearchParams(searchParams);
}
areTimeParametersValid(timeParameters) {

View File

@@ -25,118 +25,306 @@ import {
} from 'utils/testing';
describe("The URLTimeSettingsSynchronizer", () => {
let appHolder;
let openmct;
let resolveFunction;
let oldHash;
let testClock;
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalTimeSystem());
openmct.install(openmct.plugins.UTCTimeSystem());
testClock = jasmine.createSpyObj("testClock", ["start", "stop", "tick", "currentValue", "on", "off"]);
testClock.key = "test-clock";
testClock.currentValue.and.returnValue(0);
openmct.time.addClock(testClock);
openmct.on('start', done);
appHolder = document.createElement("div");
openmct.start(appHolder);
openmct.startHeadless();
});
afterEach(() => {
openmct.time.stopClock();
openmct.router.removeListener('change:hash', resolveFunction);
afterEach(() => resetApplicationState(openmct));
appHolder = undefined;
openmct = undefined;
resolveFunction = undefined;
return resetApplicationState(openmct);
});
it("initial clock is set to fixed is reflected in URL", (done) => {
resolveFunction = () => {
oldHash = window.location.hash;
describe("realtime mode", () => {
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
};
openmct.router.on('change:hash', resolveFunction);
});
it("when the clock is set via the time API, it is reflected in the URL", (done) => {
let success;
resolveFunction = () => {
openmct.time.clock('local', {
start: -1000,
end: 100
});
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
});
it("when offsets are set via the time API, they are immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.startDelta')).toBe(false);
expect(window.location.hash.includes('tc.endDelta')).toBe(false);
openmct.time.clock('local', {
start: -1000,
end: 100
});
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
openmct.time.clockOffsets({
start: -2000,
end: 200
});
expect(window.location.hash.includes('tc.startDelta=2000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=200')).toBe(true);
const hasStartDelta = window.location.hash.includes('tc.startDelta=2000');
const hasEndDelta = window.location.hash.includes('tc.endDelta=200');
const hasLocalClock = window.location.hash.includes('tc.mode=local');
success = hasStartDelta && hasEndDelta && hasLocalClock;
if (success) {
expect(success).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
});
describe("when set in the url", () => {
it("will change from fixed to realtime mode when the mode changes", () => {
expectLocationToBeInFixedMode();
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
return switchToRealtimeMode().then(() => {
let clock = openmct.time.clock();
openmct.router.on('change:hash', resolveFunction);
expect(clock).toBeDefined();
expect(clock.key).toBe('local');
});
});
it("the clock is correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clock', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.mode=local', 'tc.mode=test-clock');
window.location.hash = hash;
}).then(() => {
let clock = openmct.time.clock();
expect(clock).toBeDefined();
expect(clock.key).toBe('test-clock');
openmct.time.off('clock', resolveFunction);
});
});
});
it("the clock offsets are correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clockOffsets', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.startDelta=1000', 'tc.startDelta=2000');
hash = hash.replace('tc.endDelta=100', 'tc.endDelta=200');
window.location.hash = hash;
}).then(() => {
let clockOffsets = openmct.time.clockOffsets();
expect(clockOffsets).toBeDefined();
expect(clockOffsets.start).toBe(-2000);
expect(clockOffsets.end).toBe(200);
openmct.time.off('clockOffsets', resolveFunction);
});
});
});
it("the time system is correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('timeSystem', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
window.location.hash = hash;
}).then(() => {
let timeSystem = openmct.time.timeSystem();
expect(timeSystem).toBeDefined();
expect(timeSystem.key).toBe('local');
openmct.time.off('timeSystem', resolveFunction);
});
});
});
});
});
describe("fixed timespan mode", () => {
beforeEach(() => {
openmct.time.stopClock();
openmct.time.timeSystem('utc', {
start: 0,
end: 1
});
});
it("when bounds are set via the time API, they are immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
openmct.time.bounds({
start: 10,
end: 20
});
expect(window.location.hash.includes('tc.startBound=10')).toBe(true);
expect(window.location.hash.includes('tc.endBound=20')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.startBound=0')).toBe(false);
expect(window.location.hash.includes('tc.endBound=1')).toBe(false);
});
it("when time system is set via the time API, it is immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(true);
openmct.time.timeSystem('local', {
start: 20,
end: 30
});
expect(window.location.hash.includes('tc.timeSystem=local')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(false);
});
describe("when set in the url", () => {
it("time system changes are reflected in the API", () => {
let resolveFunction;
return new Promise((resolve) => {
let timeSystem = openmct.time.timeSystem();
resolveFunction = resolve;
expect(timeSystem.key).toBe('utc');
window.location.hash = window.location.hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
openmct.time.on('timeSystem', resolveFunction);
}).then(() => {
let timeSystem = openmct.time.timeSystem();
expect(timeSystem.key).toBe('local');
openmct.time.off('timeSystem', resolveFunction);
});
});
it("mode can be changed from realtime to fixed", () => {
return switchToRealtimeMode().then(() => {
expectLocationToBeInRealtimeMode();
expect(openmct.time.clock()).toBeDefined();
}).then(switchToFixedMode).then(() => {
let clock = openmct.time.clock();
expect(clock).not.toBeDefined();
});
});
it("bounds are correctly set in the API from the URL parameters", () => {
let resolveFunction;
expectLocationToBeInFixedMode();
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('bounds', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.startBound=0', 'tc.startBound=222')
.replace('tc.endBound=1', 'tc.endBound=333');
window.location.hash = hash;
}).then(() => {
let bounds = openmct.time.bounds();
expect(bounds).toBeDefined();
expect(bounds.start).toBe(222);
expect(bounds.end).toBe(333);
});
});
it("bounds are correctly set in the API from the URL parameters where only the end bound changes", () => {
let resolveFunction;
expectLocationToBeInFixedMode();
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('bounds', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.endBound=1', 'tc.endBound=333');
window.location.hash = hash;
}).then(() => {
let bounds = openmct.time.bounds();
expect(bounds).toBeDefined();
expect(bounds.start).toBe(0);
expect(bounds.end).toBe(333);
});
});
});
});
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
let success;
function setRealtimeLocationParameters() {
let hash = window.location.hash.toString()
.replace('tc.mode=fixed', 'tc.mode=local')
.replace('tc.startBound=0', 'tc.startDelta=1000')
.replace('tc.endBound=1', 'tc.endDelta=100');
resolveFunction = () => {
let hash = window.location.hash;
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
window.location.hash = hash;
window.location.hash = hash;
}
success = window.location.hash.includes('tc.mode=local');
if (success) {
expect(success).toBe(true);
done();
}
};
function setFixedLocationParameters() {
let hash = window.location.hash.toString()
.replace('tc.mode=local', 'tc.mode=fixed')
.replace('tc.timeSystem=utc', 'tc.timeSystem=local')
.replace('tc.startDelta=1000', 'tc.startBound=50')
.replace('tc.endDelta=100', 'tc.endBound=60');
openmct.router.on('change:hash', resolveFunction);
});
window.location.hash = hash;
}
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
let success;
function switchToRealtimeMode() {
let resolveFunction;
resolveFunction = () => {
let hash = window.location.hash;
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('clock', resolveFunction);
setRealtimeLocationParameters();
}).then(() => {
openmct.time.off('clock', resolveFunction);
});
}
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
window.location.hash = hash;
success = window.location.hash.includes('tc.mode=local');
if (success) {
expect(success).toBe(true);
done();
}
};
function switchToFixedMode() {
let resolveFunction;
openmct.router.on('change:hash', resolveFunction);
});
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clock', resolveFunction);
setFixedLocationParameters();
}).then(() => {
openmct.time.off('clock', resolveFunction);
});
}
it("reset hash", (done) => {
let success;
function expectLocationToBeInRealtimeMode() {
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
}
window.location.hash = oldHash;
resolveFunction = () => {
success = window.location.hash === oldHash;
if (success) {
expect(success).toBe(true);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
function expectLocationToBeInFixedMode() {
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
expect(window.location.hash.includes('tc.mode=local')).toBe(false);
}
});

View File

@@ -52,6 +52,7 @@
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject"
class="c-object-label icon-conditional"
:href="navigateToPath"
@click="navigateOrPreview"
>
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
@@ -285,8 +286,6 @@ export default {
if (this.openmct.editor.isEditing()) {
event.preventDefault();
this.previewAction.invoke(this.objectPath);
} else {
this.openmct.router.navigate(this.navigateToPath);
}
},
removeConditionSet() {

View File

@@ -66,6 +66,7 @@
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject"
class="c-object-label"
:href="navigateToPath"
@click="navigateOrPreview"
>
<span class="c-object-label__type-icon icon-conditional"></span>
@@ -308,8 +309,6 @@ export default {
if (this.openmct.editor.isEditing()) {
event.preventDefault();
this.previewAction.invoke(this.objectPath);
} else {
this.openmct.router.navigate(this.navigateToPath);
}
},
isItemType(type, item) {

View File

@@ -46,7 +46,7 @@ xdescribe("the plugin", () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it('installs the new folder action', () => {

View File

@@ -112,7 +112,7 @@ describe("The Duplicate Action plugin", () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it("should be defined", () => {

View File

@@ -5,7 +5,7 @@
'is-alias': item.isAlias === true,
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
}, statusClass]"
@click="navigate"
:href="objectLink"
>
<div
class="c-grid-item__type-icon"
@@ -49,17 +49,11 @@ import statusListener from './status-listener';
export default {
mixins: [contextMenuGesture, objectLink, statusListener],
inject: ['openmct'],
props: {
item: {
type: Object,
required: true
}
},
methods: {
navigate() {
this.openmct.router.navigate(this.objectLink);
}
}
};
</script>

View File

@@ -11,7 +11,7 @@
ref="objectLink"
class="c-object-label"
:class="[statusClass]"
@click="navigate"
:href="objectLink"
>
<div
class="c-object-label__type-icon c-list-item__name__type-icon"
@@ -45,7 +45,6 @@ import statusListener from './status-listener';
export default {
mixins: [contextMenuGesture, objectLink, statusListener],
inject: ['openmct'],
props: {
item: {
type: Object,
@@ -57,7 +56,7 @@ export default {
return moment(timestamp).format(format);
},
navigate() {
this.openmct.router.navigate(this.objectLink);
this.$refs.objectLink.click();
}
}
};

View File

@@ -19,10 +19,6 @@
margin: 0 $interiorMargin $interiorMargin 0;
}
}
body.mobile & {
flex: 1 0 auto;
}
}
/******************************* GRID ITEMS */

View File

@@ -41,7 +41,7 @@ export default class GoToOriginalAction {
.slice(1)
.join('/');
this._openmct.router.navigate(url);
window.location.href = url;
});
}
appliesTo(objectPath) {

View File

@@ -47,6 +47,7 @@ describe("the plugin", () => {
});
describe('when invoked', () => {
beforeEach(() => {
mockObjectPath = [{
name: 'mock folder',
@@ -62,15 +63,11 @@ describe("the plugin", () => {
key: 'test'
}
}));
goToFolderAction.invoke(mockObjectPath);
});
it('goes to the original location', (done) => {
setTimeout(() => {
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
done();
}, 1500);
it('goes to the original location', () => {
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
});
});
});

View File

@@ -32,7 +32,7 @@
:camera-pan="cameraPan"
/>
<CompassRose
v-if="hasCameraFieldOfView"
v-if="true"
:heading="heading"
:sized-image-width="sizedImageDimensions.width"
:sun-heading="sunHeading"

View File

@@ -390,9 +390,7 @@ export default {
delete this.unsubscribe;
}
if (this.imageContainerResizeObserver) {
this.imageContainerResizeObserver.disconnect();
}
this.imageContainerResizeObserver.disconnect();
if (this.relatedTelemetry.hasRelatedTelemetry) {
this.relatedTelemetry.destroy();
@@ -704,7 +702,7 @@ export default {
window.clearInterval(this.durationTracker);
},
updateDuration() {
let currentTime = this.openmct.time.clock() && this.openmct.time.clock().currentValue();
let currentTime = this.openmct.time.clock().currentValue();
this.numericDuration = currentTime - this.parsedSelectedTime;
},
resetAgeCSS() {

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import ImageryPlugin from './plugin.js';
import Vue from 'vue';
import {
createOpenMct,
@@ -89,11 +89,15 @@ describe("The Imagery View Layout", () => {
const START = Date.now();
const COUNT = 10;
let resolveFunction;
let openmct;
let imageryPlugin;
let parent;
let child;
let timeFormat = 'utc';
let bounds = {
start: START - TEN_MINUTES,
end: START
};
let imageTelemetry = generateTelemetry(START - TEN_MINUTES, COUNT);
let imageryObject = {
identifier: {
@@ -201,10 +205,6 @@ describe("The Imagery View Layout", () => {
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalTimeSystem());
openmct.install(openmct.plugins.UTCTimeSystem());
parent = document.createElement('div');
child = document.createElement('div');
parent.appendChild(child);
@@ -215,18 +215,22 @@ describe("The Imagery View Layout", () => {
});
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
imageryPlugin = new ImageryPlugin();
openmct.install(imageryPlugin);
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
openmct.time.timeSystem(timeFormat, {
start: 0,
end: 4
});
openmct.on('start', done);
openmct.start(appHolder);
openmct.startHeadless(appHolder);
});
afterEach(() => {
openmct.time.timeSystem('utc', {
start: 0,
end: 1
});
return resetApplicationState(openmct);
});
@@ -244,7 +248,7 @@ describe("The Imagery View Layout", () => {
let imageryViewProvider;
let imageryView;
beforeEach(async () => {
beforeEach(async (done) => {
let telemetryRequestResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
@@ -256,18 +260,23 @@ describe("The Imagery View Layout", () => {
return telemetryRequestPromise;
});
openmct.time.clock('local', {
start: bounds.start,
end: bounds.end + 100
});
applicableViews = openmct.objectViews.get(imageryObject, []);
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
imageryView = imageryViewProvider.view(imageryObject);
imageryView.show(child);
await telemetryRequestPromise;
await Vue.nextTick();
return done();
});
afterEach(() => {
openmct.time.stopClock();
openmct.router.removeListener('change:hash', resolveFunction);
imageryView.destroy();
});
@@ -277,44 +286,43 @@ describe("The Imagery View Layout", () => {
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
});
it("should show the clicked thumbnail as the main image", (done) => {
it("should show the clicked thumbnail as the main image", async () => {
const target = imageTelemetry[5].url;
parent.querySelectorAll(`img[src='${target}']`)[0].click();
Vue.nextTick(() => {
const imageInfo = getImageInfo(parent);
await Vue.nextTick();
const imageInfo = getImageInfo(parent);
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
});
it("should show that an image is new", async (done) => {
await Vue.nextTick();
// used in code, need to wait to the 500ms here too
setTimeout(() => {
const imageIsNew = isNew(parent);
expect(imageIsNew).toBeTrue();
done();
});
}, REFRESH_CSS_MS);
});
xit("should show that an image is new", (done) => {
Vue.nextTick(() => {
// used in code, need to wait to the 500ms here too
setTimeout(() => {
const imageIsNew = isNew(parent);
expect(imageIsNew).toBeTrue();
done();
}, REFRESH_CSS_MS);
});
});
xit("should show that an image is not new", (done) => {
it("should show that an image is not new", async (done) => {
const target = imageTelemetry[2].url;
parent.querySelectorAll(`img[src='${target}']`)[0].click();
Vue.nextTick(() => {
// used in code, need to wait to the 500ms here too
setTimeout(() => {
const imageIsNew = isNew(parent);
await Vue.nextTick();
expect(imageIsNew).toBeFalse();
done();
}, REFRESH_CSS_MS);
});
// used in code, need to wait to the 500ms here too
setTimeout(() => {
const imageIsNew = isNew(parent);
expect(imageIsNew).toBeFalse();
done();
}, REFRESH_CSS_MS);
});
it("should navigate via arrow keys", (done) => {
it("should navigate via arrow keys", async () => {
let keyOpts = {
element: parent.querySelector('.c-imagery'),
key: 'ArrowLeft',
@@ -324,15 +332,14 @@ describe("The Imagery View Layout", () => {
simulateKeyEvent(keyOpts);
Vue.nextTick(() => {
const imageInfo = getImageInfo(parent);
await Vue.nextTick();
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
done();
});
const imageInfo = getImageInfo(parent);
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
});
it("should navigate via numerous arrow keys", (done) => {
it("should navigate via numerous arrow keys", async () => {
let element = parent.querySelector('.c-imagery');
let type = 'keyup';
let leftKeyOpts = {
@@ -355,12 +362,12 @@ describe("The Imagery View Layout", () => {
// right once
simulateKeyEvent(rightKeyOpts);
Vue.nextTick(() => {
const imageInfo = getImageInfo(parent);
await Vue.nextTick();
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
done();
});
const imageInfo = getImageInfo(parent);
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
});
});
});

View File

@@ -55,7 +55,7 @@ describe("The local time", () => {
beforeEach(() => {
localTimeSystem = openmct.time.timeSystem(LOCAL_SYSTEM_KEY, {
start: 0,
end: 1
end: 4
});
});

View File

@@ -81,7 +81,7 @@ describe("The Move Action plugin", () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it("should be defined", () => {

View File

@@ -135,7 +135,6 @@ import SearchResults from './SearchResults.vue';
import Sidebar from './Sidebar.vue';
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
import objectUtils from 'objectUtils';
import { debounce } from 'lodash';
@@ -190,14 +189,14 @@ export default {
selectedPage() {
const pages = this.getPages();
if (!pages) {
return {};
return null;
}
return pages.find(page => page.isSelected);
},
selectedSection() {
if (!this.sections.length) {
return {};
return null;
}
return this.sections.find(section => section.isSelected);
@@ -217,7 +216,6 @@ export default {
window.addEventListener('orientationchange', this.formatSidebar);
window.addEventListener("hashchange", this.navigateToSectionPage, false);
this.openmct.router.on('change:params', this.changeSectionPage);
this.navigateToSectionPage();
},
@@ -228,7 +226,6 @@ export default {
window.removeEventListener('orientationchange', this.formatSidebar);
window.removeEventListener("hashchange", this.navigateToSectionPage);
this.openmct.router.off('change:params', this.changeSectionPage);
},
updated: function () {
this.$nextTick(() => {
@@ -236,28 +233,6 @@ export default {
});
},
methods: {
changeSectionPage(newParams, oldParams, changedParams) {
if (newParams.view !== NOTEBOOK_VIEW_TYPE) {
return;
}
let pageId = newParams.pageId;
let sectionId = newParams.sectionId;
if (!pageId && !sectionId) {
return;
}
this.sections.forEach(section => {
section.isSelected = Boolean(section.id === sectionId);
if (section.isSelected) {
section.pages.forEach(page => {
page.isSelected = Boolean(page.id === pageId);
});
}
});
},
changeSelectedSection({ sectionId, pageId }) {
const sections = this.sections.map(s => {
s.isSelected = false;
@@ -543,11 +518,9 @@ export default {
return this.sections.find(section => section.isSelected);
},
navigateToSectionPage() {
let { pageId, sectionId } = this.openmct.router.getParams();
const { pageId, sectionId } = this.openmct.router.getParams();
if (!pageId || !sectionId) {
sectionId = this.selectedSection.id;
pageId = this.selectedPage.id;
return;
}
const sections = this.sections.map(s => {

View File

@@ -145,7 +145,7 @@ export default {
const relativeHash = hash.slice(hash.indexOf('#'));
const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`);
this.openmct.router.navigate(url.hash);
window.location.hash = url.hash;
},
formatTime(unixTime, timeFormat) {
return Moment.utc(unixTime).format(timeFormat);

View File

@@ -111,6 +111,10 @@ export default {
}
}
},
data() {
return {
};
},
computed: {
pages() {
const selectedSection = this.sections.find(section => section.isSelected);

View File

@@ -1,4 +1,3 @@
export const EVENT_SNAPSHOTS_UPDATED = 'SNAPSHOTS_UPDATED';
export const NOTEBOOK_DEFAULT = 'DEFAULT';
export const NOTEBOOK_SNAPSHOT = 'SNAPSHOT';
export const NOTEBOOK_VIEW_TYPE = 'notebook-vue';

View File

@@ -65,8 +65,7 @@ describe("Notebook plugin:", () => {
afterAll(() => {
appHolder.remove();
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it("has type as Notebook", () => {

View File

@@ -140,8 +140,7 @@ describe('Notebook Entries:', () => {
afterEach(() => {
notebookDomainObject.configuration.entries[selectedSection.id][selectedPage.id] = [];
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it('getNotebookEntries has no entries', () => {

View File

@@ -83,7 +83,7 @@ describe('Notebook Storage:', () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it('has empty local Storage', () => {

View File

@@ -117,7 +117,7 @@ describe('the plugin', () => {
});
});
it('updates an object', (done) => {
it('updates an object', () => {
return openmct.objects.save(mockDomainObject).then((result) => {
expect(result).toBeTrue();
expect(provider.create).toHaveBeenCalled();
@@ -128,7 +128,6 @@ describe('the plugin', () => {
return openmct.objects.save(mockDomainObject).then((updatedResult) => {
expect(updatedResult).toBeTrue();
expect(provider.update).toHaveBeenCalled();
done();
});
});
});

View File

@@ -28,7 +28,6 @@
:series="config.series.models"
:highlights="highlights"
:legend="config.legend"
@legendHoverChanged="legendHoverChanged"
/>
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
<y-axis v-if="config.series.models.length > 0"
@@ -68,7 +67,6 @@
>
<mct-chart :rectangles="rectangles"
:highlights="highlights"
:show-limit-line-labels="showLimitLineLabels"
@plotReinitializeCanvas="initCanvas"
/>
</div>
@@ -161,7 +159,6 @@ import MctTicks from "./MctTicks.vue";
import MctChart from "./chart/MctChart.vue";
import XAxis from "./axis/XAxis.vue";
import YAxis from "./axis/YAxis.vue";
import _ from "lodash";
export default {
components: {
@@ -215,8 +212,7 @@ export default {
pending: 0,
isRealTime: this.openmct.time.clock() !== undefined,
loaded: false,
isTimeOutOfSync: false,
showLimitLineLabels: undefined
isTimeOutOfSync: false
};
},
computed: {
@@ -500,10 +496,6 @@ export default {
},
initialize() {
_.debounce(this.handleWindowResize, 400);
this.plotContainerResizeObserver = new ResizeObserver(this.handleWindowResize);
this.plotContainerResizeObserver.observe(this.$parent.$refs.plotWrapper);
// Setup canvas etc.
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
@@ -1007,23 +999,12 @@ export default {
this.removeStatusListener();
}
this.plotContainerResizeObserver.disconnect();
this.openmct.time.off('clock', this.updateRealTime);
this.openmct.time.off('bounds', this.updateDisplayBounds);
this.openmct.objectViews.off('clearData', this.clearData);
},
updateStatus(status) {
this.$emit('statusUpdated', status);
},
handleWindowResize() {
if (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth) {
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
this.config.series.models.forEach(this.loadSeriesData, this);
}
},
legendHoverChanged(data) {
this.showLimitLineLabels = data;
}
}
};

View File

@@ -228,16 +228,15 @@ export default {
doTickUpdate() {
if (this.shouldCheckWidth) {
const tickElements = this.$refs.tickContainer && this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
if (tickElements) {
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
return Math.max(memo, first.offsetWidth);
}, 0));
const tickElements = this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
this.tickWidth = tickWidth;
this.$emit('plotTickWidth', tickWidth);
this.shouldCheckWidth = false;
}
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
return Math.max(memo, first.offsetWidth);
}, 0));
this.tickWidth = tickWidth;
this.$emit('plotTickWidth', tickWidth);
this.shouldCheckWidth = false;
}
this.tickUpdate = false;

View File

@@ -1,46 +0,0 @@
<template>
<div class="plot-series-limit-label"
:style="styleObj"
:class="limit.cssClass"
>
<span class="plot-series-limit-value">{{ limit.value }}</span>
<span class="plot-series-color-swatch"
:style="{ 'background-color': limit.color }"
></span>
<span class="plot-series-name">{{ limit.name }}</span>
</div>
</template>
<script>
export default {
props: {
limit: {
type: Object,
required: true,
default() {
return {};
}
},
point: {
type: Object,
required: true,
default() {
return {};
}
}
},
computed: {
styleObj() {
const top = `${this.point.top - 10}px`;
const left = `${this.point.left + 5}px`;
return {
'position': 'absolute',
'top': top,
'left': left,
'color': '#fff'
};
}
}
};
</script>

View File

@@ -1,44 +0,0 @@
<template>
<hr :style="styleObj"
:class="cssWithoutUprLwr"
>
</template>
<script>
export default {
props: {
point: {
type: Object,
required: true,
default() {
return {};
}
},
cssClass: {
type: String,
default() {
return '';
}
}
},
computed: {
styleObj() {
const top = `${this.point.top}px`;
const left = `${this.point.left}px`;
return {
'position': 'absolute',
'width': '100%',
'top': top,
'left': left
};
},
cssWithoutUprLwr() {
let cssClass = this.cssClass.replace(/is-limit--upr/gi, 'is-limit--line');
cssClass = cssClass.replace(/is-limit--lwr/gi, 'is-limit--line');
return cssClass;
}
}
};
</script>

View File

@@ -1,105 +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 eventHelpers from '../lib/eventHelpers';
export default class MCTChartAlarmLineSet {
constructor(series, chart, offset, bounds) {
this.series = series;
this.chart = chart;
this.offset = offset;
this.bounds = bounds;
this.limits = [];
eventHelpers.extend(this);
this.listenTo(series, 'limitBounds', this.updateBounds, this);
this.listenTo(series, 'change:xKey', this.getLimitPoints, this);
if (series.limits) {
this.getLimitPoints(series);
}
}
updateBounds(bounds) {
this.bounds = bounds;
this.getLimitPoints(this.series);
}
color() {
return this.series.get('color');
}
name() {
return this.series.get('name');
}
makePoint(point, series) {
if (!this.offset.xVal) {
this.chart.setOffset(point, undefined, series);
}
return {
x: this.offset.xVal(point, series),
y: this.offset.yVal(point, series)
};
}
getLimitPoints(series) {
this.limits = [];
let xKey = series.get('xKey');
Object.keys(series.limits).forEach((key) => {
const limitForLevel = series.limits[key];
if (limitForLevel.high) {
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.high), series);
this.limits.push({
seriesKey: series.keyString,
value: series.getYVal(limitForLevel.high),
color: this.color().asHexString(),
name: this.name(),
point,
cssClass: limitForLevel.high.cssClass
});
}
if (limitForLevel.low) {
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.low), series);
this.limits.push({
seriesKey: series.keyString,
value: series.getYVal(limitForLevel.low),
color: this.color().asHexString(),
name: this.name(),
point,
cssClass: limitForLevel.low.cssClass
});
}
}, this);
}
reset() {
this.limits = [];
}
destroy() {
this.stopListening();
}
}

View File

@@ -23,9 +23,6 @@
<div class="gl-plot-chart-area">
<span v-html="canvasTemplate"></span>
<span v-html="canvasTemplate"></span>
<div ref="limitArea"
class="js-limit-area"
></div>
</div>
</template>
@@ -37,12 +34,8 @@ import MCTChartLineLinear from './MCTChartLineLinear';
import MCTChartLineStepAfter from './MCTChartLineStepAfter';
import MCTChartPointSet from './MCTChartPointSet';
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
import configStore from "../configuration/configStore";
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
import LimitLine from "./LimitLine.vue";
import LimitLabel from "./LimitLabel.vue";
import Vue from 'vue';
const MARKER_SIZE = 6.0;
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
@@ -61,12 +54,6 @@ export default {
default() {
return [];
}
},
showLimitLineLabels: {
type: Object,
default() {
return {};
}
}
},
data() {
@@ -80,22 +67,18 @@ export default {
},
rectangles() {
this.scheduleDraw();
},
showLimitLineLabels() {
this.drawLimitLines();
}
},
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.isDestroyed = false;
this.lines = [];
this.limitLines = [];
this.pointSets = [];
this.alarmSets = [];
this.offset = {};
this.seriesElements = new WeakMap();
this.seriesLimits = new WeakMap();
let canvasEls = this.$parent.$refs.chartContainer.querySelectorAll("canvas");
const mainCanvas = canvasEls[1];
@@ -107,8 +90,8 @@ export default {
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
},
beforeDestroy() {
@@ -133,18 +116,15 @@ export default {
this.changeInterpolate(mode, o, series);
this.changeMarkers(mode, o, series);
this.changeAlarmMarkers(mode, o, series);
this.changeLimitLines(mode, o, series);
},
onSeriesAdd(series) {
this.listenTo(series, 'change:xKey', this.reDraw, this);
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
this.listenTo(series, 'change:markers', this.changeMarkers, this);
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
this.listenTo(series, 'change', this.scheduleDraw);
this.listenTo(series, 'add', this.scheduleDraw);
this.makeChartElement(series);
this.makeLimitLines(series);
},
changeInterpolate(mode, o, series) {
if (mode === o) {
@@ -198,14 +178,6 @@ export default {
this.pointSets.push(pointSet);
}
},
changeLimitLines(mode, o, series) {
if (mode === o) {
return;
}
this.makeLimitLines(series);
this.updateLimitsAndDraw();
},
onSeriesRemove(series) {
this.stopListening(series);
this.removeChartElement(series);
@@ -215,7 +187,6 @@ export default {
this.isDestroyed = true;
this.stopListening();
this.lines.forEach(line => line.destroy());
this.limitLines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI);
},
clearOffset() {
@@ -228,9 +199,6 @@ export default {
this.lines.forEach(function (line) {
line.reset();
});
this.limitLines.forEach(function (line) {
line.reset();
});
this.pointSets.forEach(function (pointSet) {
pointSet.reset();
});
@@ -301,8 +269,6 @@ export default {
}
this.seriesElements.delete(series);
this.clearLimitLines(series);
},
lineForSeries(series) {
if (series.get('interpolate') === 'linear') {
@@ -321,14 +287,6 @@ export default {
);
}
},
limitLineForSeries(series) {
return new MCTChartAlarmLineSet(
series,
this,
this.offset,
this.openmct.time.bounds()
);
},
pointSetForSeries(series) {
if (series.get('markers')) {
return new MCTChartPointSet(
@@ -350,8 +308,7 @@ export default {
makeChartElement(series) {
const elements = {
lines: [],
pointSets: [],
limitLines: []
pointSets: []
};
const line = this.lineForSeries(series);
@@ -373,37 +330,6 @@ export default {
this.seriesElements.set(series, elements);
},
makeLimitLines(series) {
this.clearLimitLines(series);
if (!series.get('limitLines')) {
return;
}
const limitElements = {
limitLines: []
};
const limitLine = this.limitLineForSeries(series);
if (limitLine) {
limitElements.limitLines.push(limitLine);
this.limitLines.push(limitLine);
}
this.seriesLimits.set(series, limitElements);
},
clearLimitLines(series) {
const seriesLimits = this.seriesLimits.get(series);
if (seriesLimits) {
seriesLimits.limitLines.forEach(function (line) {
this.limitLines.splice(this.limitLines.indexOf(line), 1);
line.destroy();
}, this);
this.seriesLimits.delete(series);
}
},
canDraw() {
if (!this.offset.x || !this.offset.y) {
return false;
@@ -411,10 +337,6 @@ export default {
return true;
},
updateLimitsAndDraw() {
this.drawLimitLines();
this.scheduleDraw();
},
scheduleDraw() {
if (!this.drawScheduled) {
requestAnimationFrame(this.draw);
@@ -463,68 +385,6 @@ export default {
this.pointSets.forEach(this.drawPoints, this);
this.alarmSets.forEach(this.drawAlarmPoints, this);
},
drawLimitLines() {
if (this.canDraw()) {
this.updateViewport();
if (!this.drawAPI.origin) {
return;
}
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
this.limitLines.forEach((limitLine) => {
let limitContainerEl = this.$refs.limitArea;
limitLine.limits.forEach((limit) => {
const showLabels = this.showLabels(limit.seriesKey);
if (showLabels) {
let limitLabelEl = this.getLimitLabel(limit);
limitContainerEl.appendChild(limitLabelEl);
}
let limitEl = this.getLimitElement(limit);
limitContainerEl.appendChild(limitEl);
}, this);
});
}
},
showLabels(seriesKey) {
return this.showLimitLineLabels.seriesKey
&& (this.showLimitLineLabels.seriesKey === seriesKey);
},
getLimitElement(limit) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLineClass = Vue.extend(LimitLine);
const component = new LimitLineClass({
propsData: {
point,
cssClass: limit.cssClass
}
});
component.$mount();
return component.$el;
},
getLimitLabel(limit) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLabelClass = Vue.extend(LimitLabel);
const component = new LimitLabelClass({
propsData: {
limit,
point
}
});
component.$mount();
return component.$el;
},
drawAlarmPoints(alarmSet) {
this.drawAPI.drawLimitPoints(
alarmSet.points,
@@ -541,12 +401,11 @@ export default {
chartElement.series.get('markerShape')
);
},
drawLine(chartElement, disconnected) {
drawLine(chartElement) {
this.drawAPI.drawLine(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count,
disconnected
chartElement.count
);
},
drawHighlights() {

View File

@@ -97,8 +97,7 @@ export default class PlotSeries extends Model {
markers: true,
markerShape: 'point',
markerSize: 2.0,
alarmMarkers: true,
limitLines: false
alarmMarkers: true
};
}
@@ -116,16 +115,9 @@ export default class PlotSeries extends Model {
this.domainObject = options.domainObject;
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
this.limits = this.limitDefinition.limits();
this.openmct.time.on('bounds', this.updateLimits);
this.on('destroy', this.onDestroy, this);
}
updateLimits(bounds) {
this.emit('limitBounds', bounds);
}
locateOldObject(oldStyleParent) {
return oldStyleParent.useCapability('composition')
.then(function (children) {

View File

@@ -60,14 +60,6 @@
{{ alarmMarkers ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display lines visually denoting alarm limits."
>Limit lines</div>
<div class="grid-cell value">
{{ limitLines ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The plot line and marker color for this series."
@@ -143,9 +135,6 @@ export default {
alarmMarkers() {
return this.series.get('alarmMarkers');
},
limitLines() {
return this.series.get('limitLines');
},
seriesHexColor() {
return this.series.get('color').asHexString();
}

View File

@@ -91,17 +91,6 @@
>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display limit lines"
>Limit lines</div>
<div class="grid-cell value">
<input v-model="limitLines"
type="checkbox"
@change="updateForm('limitLines')"
>
</div>
</li>
<li v-show="markers || alarmMarkers"
class="grid-row"
>
@@ -179,7 +168,6 @@ export default {
markers: this.series.get('markers'),
markerShape: this.series.get('markerShape'),
alarmMarkers: this.series.get('alarmMarkers'),
limitLines: this.series.get('limitLines'),
markerSize: this.series.get('markerSize'),
validation: {},
swatchActive: false
@@ -252,11 +240,6 @@ export default {
modelProp: 'alarmMarkers',
coerce: Boolean,
objectPath: this.dynamicPathForKey('alarmMarkers')
},
{
modelProp: 'limitLines',
coerce: Boolean,
objectPath: this.dynamicPathForKey('limitLines')
}
];

View File

@@ -48,7 +48,7 @@
:highlights="highlights"
:value-to-show-when-collapsed="legend.get('valueToShowWhenCollapsed')"
:series-object="seriesObject"
@legendHoverChanged="legendHoverChanged"
:closest="seriesObject.closest"
/>
</div>
<!-- EXPANDED PLOT LEGEND -->
@@ -89,7 +89,6 @@
:series-object="seriesObject"
:highlights="highlights"
:legend="legend"
@legendHoverChanged="legendHoverChanged"
/>
</tbody>
</table>
@@ -134,36 +133,19 @@ export default {
},
data() {
return {
isLegendExpanded: this.legend.get('expanded') === true
isLegendHidden: this.legend.get('hideLegendWhenSmall') !== true,
isLegendExpanded: this.legend.get('expanded') === true,
showTimestampWhenExpanded: this.legend.get('showTimestampWhenExpanded') === true,
showValueWhenExpanded: this.legend.get('showValueWhenExpanded') === true,
showUnitsWhenExpanded: this.legend.get('showUnitsWhenExpanded') === true,
showMinimumWhenExpanded: this.legend.get('showMinimumWhenExpanded') === true,
showMaximumWhenExpanded: this.legend.get('showMaximumWhenExpanded') === true
};
},
computed: {
showUnitsWhenExpanded() {
return this.legend.get('showUnitsWhenExpanded') === true;
},
showMinimumWhenExpanded() {
return this.legend.get('showMinimumWhenExpanded') === true;
},
showMaximumWhenExpanded() {
return this.legend.get('showMaximumWhenExpanded') === true;
},
showValueWhenExpanded() {
return this.legend.get('showValueWhenExpanded') === true;
},
showTimestampWhenExpanded() {
return this.legend.get('showTimestampWhenExpanded') === true;
},
isLegendHidden() {
return this.legend.get('hideLegendWhenSmall') === true;
}
},
methods: {
expandLegend() {
this.isLegendExpanded = !this.isLegendExpanded;
this.legend.set('expanded', this.isLegendExpanded);
},
legendHoverChanged(data) {
this.$emit('legendHoverChanged', data);
}
}
};

View File

@@ -24,8 +24,6 @@
:class="{
'is-status--missing': isMissing
}"
@mouseover="toggleHover(true)"
@mouseleave="toggleHover(false)"
>
<div class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
@@ -123,12 +121,6 @@ export default {
} else {
this.formattedYValueFromStats = '';
}
},
toggleHover(hover) {
this.hover = hover;
this.$emit('legendHoverChanged', {
seriesKey: this.hover ? this.seriesObject.keyString : ''
});
}
}
};

View File

@@ -25,8 +25,6 @@
:class="{
'is-status--missing': isMissing
}"
@mouseover="toggleHover(true)"
@mouseleave="toggleHover(false)"
>
<td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
@@ -96,6 +94,11 @@ export default {
},
data() {
return {
showTimestampWhenExpanded: this.legend.get('showTimestampWhenExpanded'),
showValueWhenExpanded: this.legend.get('showValueWhenExpanded'),
showUnitsWhenExpanded: this.legend.get('showUnitsWhenExpanded'),
showMinimumWhenExpanded: this.legend.get('showMinimumWhenExpanded'),
showMaximumWhenExpanded: this.legend.get('showMaximumWhenExpanded'),
isMissing: false,
colorAsHexString: '',
name: '',
@@ -107,23 +110,6 @@ export default {
mctLimitStateClass: ''
};
},
computed: {
showUnitsWhenExpanded() {
return this.legend.get('showUnitsWhenExpanded') === true;
},
showMinimumWhenExpanded() {
return this.legend.get('showMinimumWhenExpanded') === true;
},
showMaximumWhenExpanded() {
return this.legend.get('showMaximumWhenExpanded') === true;
},
showValueWhenExpanded() {
return this.legend.get('showValueWhenExpanded') === true;
},
showTimestampWhenExpanded() {
return this.legend.get('showTimestampWhenExpanded') === true;
}
},
watch: {
highlights(newHighlights) {
const highlightedObject = newHighlights.find(highlight => highlight.series.keyString === this.seriesObject.keyString);
@@ -162,12 +148,6 @@ export default {
this.formattedMinY = '';
this.formattedMaxY = '';
}
},
toggleHover(hover) {
this.hover = hover;
this.$emit('legendHoverChanged', {
seriesKey: this.hover ? this.seriesObject.keyString : ''
});
}
}
};

View File

@@ -36,7 +36,6 @@ describe("the plugin", function () {
let telemetryPromise;
let cleanupFirst;
let mockObjectPath;
let telemetrylimitProvider;
beforeEach((done) => {
mockObjectPath = [
@@ -89,45 +88,6 @@ describe("the plugin", function () {
return telemetryPromise;
});
telemetrylimitProvider = jasmine.createSpyObj('telemetrylimitProvider', [
'supportsLimits',
'getLimits',
'getLimitEvaluator'
]);
telemetrylimitProvider.supportsLimits.and.returnValue(true);
telemetrylimitProvider.getLimits.and.returnValue({
limits: function () {
return {
WARNING: {
low: {
cssClass: "is-limit--lwr is-limit--yellow",
'some-key': -0.5
},
high: {
cssClass: "is-limit--upr is-limit--yellow",
'some-key': 0.5
}
},
DISTRESS: {
low: {
cssClass: "is-limit--lwr is-limit--red",
'some-key': -0.9
},
high: {
cssClass: "is-limit--upr is-limit--red",
'some-key': 0.9
}
}
};
}
});
telemetrylimitProvider.getLimitEvaluator.and.returnValue({
evaluate: function () {
return {};
}
});
openmct.telemetry.addProvider(telemetrylimitProvider);
openmct.install(new PlotVuePlugin());
element = document.createElement("div");
@@ -139,11 +99,6 @@ describe("the plugin", function () {
element.appendChild(child);
document.body.appendChild(element);
spyOn(window, 'ResizeObserver').and.returnValue({
observe() {},
disconnect() {}
});
openmct.time.timeSystem("utc", {
start: 0,
end: 4
@@ -163,11 +118,6 @@ describe("the plugin", function () {
});
afterEach((done) => {
openmct.time.timeSystem('utc', {
start: 0,
end: 1
});
// Needs to be in a timeout because plots use a bunch of setTimeouts, some of which can resolve during or after
// teardown, which causes problems
// This is hacky, we should find a better approach here.
@@ -179,7 +129,7 @@ describe("the plugin", function () {
configStore.deleteAll();
resetApplicationState(openmct).then(done).catch(done);
resetApplicationState(openmct).then(done);
});
});
@@ -697,25 +647,6 @@ describe("the plugin", function () {
done();
});
});
describe('limits', () => {
it('lines are not displayed by default', () => {
let limitEl = element.querySelectorAll(".js-limit-area hr");
expect(limitEl.length).toBe(0);
});
it('lines are displayed when configuration is set to true', (done) => {
config.series.models[0].set('limitLines', true);
Vue.nextTick(() => {
let limitEl = element.querySelectorAll(".js-limit-area hr");
expect(limitEl.length).toBe(4);
done();
});
});
});
});
describe('the inspector view', () => {
@@ -862,7 +793,7 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const plotOptionsProperties = browseOptionsEl.querySelectorAll('.js-plot-options-browse-properties .grid-row');
expect(plotOptionsProperties.length).toEqual(6);
expect(plotOptionsProperties.length).toEqual(5);
});
});
@@ -904,7 +835,7 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const plotOptionsProperties = editOptionsEl.querySelectorAll(".js-plot-options-edit-properties .grid-row");
expect(plotOptionsProperties.length).toEqual(7);
expect(plotOptionsProperties.length).toEqual(6);
});
it('shows yKeyOptions', () => {

View File

@@ -78,7 +78,7 @@ export default class RemoveAction {
.map(object => this.openmct.objects.makeKeyString(object.identifier))
.join("/");
this.openmct.router.navigate('#/browse/' + urlPath);
window.location.href = '#/browse/' + urlPath;
}
removeFromComposition(parent, child) {

View File

@@ -72,7 +72,7 @@ describe("The Remove Action plugin", () => {
});
afterEach(() => {
return resetApplicationState(openmct);
resetApplicationState(openmct);
});
it("should be defined", () => {

View File

@@ -65,6 +65,11 @@
<script>
import ObjectView from '../../../ui/components/ObjectView.vue';
import RemoveAction from '../../remove/RemoveAction.js';
import {
getSearchParam,
setSearchParam,
deleteSearchParam
} from 'utils/openmctLocation';
const unknownObjectType = {
definition: {
@@ -110,7 +115,7 @@ export default {
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.onReorder);
this.composition.load().then(() => {
let currentTabIndexFromURL = this.openmct.router.getSearchParam(this.searchTabKey);
let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
if (currentTabIndexFromURL !== null) {
@@ -124,8 +129,6 @@ export default {
this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
this.openmct.router.on('change:params', this.updateCurrentTab.bind(this));
this.RemoveAction = new RemoveAction(this.openmct);
document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend);
@@ -145,8 +148,6 @@ export default {
this.unsubscribe();
this.clearCurrentTabIndexFromURL();
this.openmct.router.off('change:params', this.updateCurrentTab.bind(this));
document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend);
},
@@ -284,15 +285,15 @@ export default {
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
},
storeCurrentTabIndexInURL(index) {
let currentTabIndexInURL = this.openmct.router.getSearchParam(this.searchTabKey);
let currentTabIndexInURL = getSearchParam(this.searchTabKey);
if (index !== currentTabIndexInURL) {
this.openmct.router.setSearchParam(this.searchTabKey, index);
setSearchParam(this.searchTabKey, index);
this.currentTabIndex = index;
}
},
clearCurrentTabIndexFromURL() {
this.openmct.router.deleteSearchParam(this.searchTabKey);
deleteSearchParam(this.searchTabKey);
},
updateStatus(keyString, status) {
let tabPos = this.tabsList.findIndex((tab) => {
@@ -308,19 +309,6 @@ export default {
} else {
return this.loadedTabs[tab.keyString];
}
},
updateCurrentTab(newParams, oldParams, changedParams) {
const tabIndex = changedParams[this.searchTabKey];
if (!tabIndex) {
return;
}
if (this.currentTabIndex === parseInt(tabIndex, 10)) {
return;
}
this.currentTabIndex = tabIndex;
this.currentTab = this.tabsList[tabIndex];
}
}
};

View File

@@ -82,11 +82,6 @@ describe("the plugin", () => {
});
afterEach(() => {
openmct.time.timeSystem('utc', {
start: 0,
end: 1
});
return resetApplicationState(openmct);
});

View File

@@ -83,9 +83,9 @@
@update="timePopUpdate"
/>
<button
ref="startOffset"
class="c-button c-conductor__delta-button"
@click="showTimePopupStart"
class="c-button c-conductor__delta-button"
ref="startOffset"
>
{{ offsets.start }}
</button>
@@ -135,7 +135,7 @@
class="c-button c-conductor__delta-button"
@click="showTimePopupEnd"
>
{{ offsets.end }}
{{offsets.end}}
</button>
</div>

View File

@@ -59,7 +59,7 @@ describe("The UTC Time System", () => {
it("can be set to be the main time system", () => {
openmct.time.timeSystem(UTC_SYSTEM_AND_FORMAT_KEY, {
start: 0,
end: 1
end: 4
});
expect(openmct.time.timeSystem().key).toBe(UTC_SYSTEM_AND_FORMAT_KEY);

View File

@@ -362,7 +362,7 @@ $legendTableHeadBg: $colorTabHeaderBg;
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(#fff, 0.1);
$colorItemTreeHoverBg: rgba(#fff, 0.03);
$colorItemTreeHoverFg: #fff;
$colorItemTreeIcon: $colorKey; // Used
$colorItemTreeIconHover: $colorItemTreeIcon; // Used

View File

@@ -212,7 +212,6 @@ $glyph-icon-3-dots: '\ea37';
$glyph-icon-grid-on: '\ea38';
$glyph-icon-grid-off: '\ea39';
$glyph-icon-camera: '\ea3a';
$glyph-icon-folders-collapse: '\ea3b';
$glyph-icon-activity: '\eb00';
$glyph-icon-activity-mode: '\eb01';
$glyph-icon-autoflow-tabular: '\eb02';

View File

@@ -144,7 +144,6 @@
.icon-grid-on { @include glyphBefore($glyph-icon-grid-on); }
.icon-grid-off { @include glyphBefore($glyph-icon-grid-off); }
.icon-camera { @include glyphBefore($glyph-icon-camera); }
.icon-folders-collapse { @include glyphBefore($glyph-icon-folders-collapse); }
.icon-activity { @include glyphBefore($glyph-icon-activity); }
.icon-activity-mode { @include glyphBefore($glyph-icon-activity-mode); }
.icon-autoflow-tabular { @include glyphBefore($glyph-icon-autoflow-tabular); }

View File

@@ -531,7 +531,7 @@ mct-plot {
}
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item, .plot-series-limit-label {
.plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states
> * + * {
margin-left: $interiorMarginSm;

View File

@@ -70,10 +70,6 @@
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
}
@mixin andLine {
&.is-limit--line:before { content: '' !important; }
}
@mixin uIndicator($bg, $fg, $glyph) {
background: $bg;
color: $fg;
@@ -104,14 +100,12 @@
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg, true);
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
@include andUprLwr();
@include andLine();
}
&.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg, true);
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
@include andUprLwr();
@include andLine();
}
}

View File

@@ -2,7 +2,7 @@
"metadata": {
"name": "Open MCT Symbols 16px",
"lastOpened": 0,
"created": 1621648023886
"created": 1602779919972
},
"iconSets": [
{
@@ -847,21 +847,13 @@
"code": 59962,
"tempChar": ""
},
{
"order": 196,
"id": 168,
"name": "icon-folders-collapse",
"prevSize": 24,
"code": 59963,
"tempChar": ""
},
{
"order": 144,
"id": 97,
"name": "icon-activity",
"prevSize": 24,
"code": 60160,
"tempChar": ""
"tempChar": ""
},
{
"order": 104,
@@ -869,7 +861,7 @@
"name": "icon-activity-mode",
"prevSize": 24,
"code": 60161,
"tempChar": ""
"tempChar": ""
},
{
"order": 137,
@@ -877,7 +869,7 @@
"name": "icon-autoflow-tabular",
"prevSize": 24,
"code": 60162,
"tempChar": ""
"tempChar": ""
},
{
"order": 115,
@@ -885,7 +877,7 @@
"name": "icon-clock",
"prevSize": 24,
"code": 60163,
"tempChar": ""
"tempChar": ""
},
{
"order": 2,
@@ -893,7 +885,7 @@
"name": "icon-database",
"prevSize": 24,
"code": 60164,
"tempChar": ""
"tempChar": ""
},
{
"order": 3,
@@ -901,7 +893,7 @@
"name": "icon-database-query",
"prevSize": 24,
"code": 60165,
"tempChar": ""
"tempChar": ""
},
{
"order": 67,
@@ -909,7 +901,7 @@
"name": "icon-dataset",
"prevSize": 24,
"code": 60166,
"tempChar": ""
"tempChar": ""
},
{
"order": 59,
@@ -917,7 +909,7 @@
"name": "icon-datatable",
"prevSize": 24,
"code": 60167,
"tempChar": ""
"tempChar": ""
},
{
"order": 136,
@@ -925,7 +917,7 @@
"name": "icon-dictionary",
"prevSize": 24,
"code": 60168,
"tempChar": ""
"tempChar": ""
},
{
"order": 51,
@@ -933,7 +925,7 @@
"name": "icon-folder",
"prevSize": 24,
"code": 60169,
"tempChar": ""
"tempChar": ""
},
{
"order": 147,
@@ -941,7 +933,7 @@
"name": "icon-image",
"prevSize": 24,
"code": 60170,
"tempChar": ""
"tempChar": ""
},
{
"order": 4,
@@ -949,7 +941,7 @@
"name": "icon-layout",
"prevSize": 24,
"code": 60171,
"tempChar": ""
"tempChar": ""
},
{
"order": 24,
@@ -957,7 +949,7 @@
"name": "icon-object",
"prevSize": 24,
"code": 60172,
"tempChar": ""
"tempChar": ""
},
{
"order": 52,
@@ -965,7 +957,7 @@
"name": "icon-object-unknown",
"prevSize": 24,
"code": 60173,
"tempChar": ""
"tempChar": ""
},
{
"order": 105,
@@ -973,7 +965,7 @@
"name": "icon-packet",
"prevSize": 24,
"code": 60174,
"tempChar": ""
"tempChar": ""
},
{
"order": 126,
@@ -981,7 +973,7 @@
"name": "icon-page",
"prevSize": 24,
"code": 60175,
"tempChar": ""
"tempChar": ""
},
{
"order": 130,
@@ -989,7 +981,7 @@
"name": "icon-plot-overlay",
"prevSize": 24,
"code": 60176,
"tempChar": ""
"tempChar": ""
},
{
"order": 80,
@@ -997,7 +989,7 @@
"name": "icon-plot-stacked",
"prevSize": 24,
"code": 60177,
"tempChar": ""
"tempChar": ""
},
{
"order": 134,
@@ -1005,7 +997,7 @@
"name": "icon-session",
"prevSize": 24,
"code": 60178,
"tempChar": ""
"tempChar": ""
},
{
"order": 109,
@@ -1013,7 +1005,7 @@
"name": "icon-tabular",
"prevSize": 24,
"code": 60179,
"tempChar": ""
"tempChar": ""
},
{
"order": 107,
@@ -1021,7 +1013,7 @@
"name": "icon-tabular-lad",
"prevSize": 24,
"code": 60180,
"tempChar": ""
"tempChar": ""
},
{
"order": 106,
@@ -1029,7 +1021,7 @@
"name": "icon-tabular-lad-set",
"prevSize": 24,
"code": 60181,
"tempChar": ""
"tempChar": ""
},
{
"order": 70,
@@ -1037,7 +1029,7 @@
"name": "icon-tabular-realtime",
"prevSize": 24,
"code": 60182,
"tempChar": ""
"tempChar": ""
},
{
"order": 60,
@@ -1045,7 +1037,7 @@
"name": "icon-tabular-scrolling",
"prevSize": 24,
"code": 60183,
"tempChar": ""
"tempChar": ""
},
{
"order": 131,
@@ -1053,7 +1045,7 @@
"name": "icon-telemetry",
"prevSize": 24,
"code": 60184,
"tempChar": ""
"tempChar": ""
},
{
"order": 108,
@@ -1061,7 +1053,7 @@
"name": "icon-timeline",
"prevSize": 24,
"code": 60185,
"tempChar": ""
"tempChar": ""
},
{
"order": 81,
@@ -1069,7 +1061,7 @@
"name": "icon-timer",
"prevSize": 24,
"code": 60186,
"tempChar": ""
"tempChar": ""
},
{
"order": 69,
@@ -1077,7 +1069,7 @@
"name": "icon-topic",
"prevSize": 24,
"code": 60187,
"tempChar": ""
"tempChar": ""
},
{
"order": 79,
@@ -1085,7 +1077,7 @@
"name": "icon-box-with-dashed-lines-v2",
"prevSize": 24,
"code": 60188,
"tempChar": ""
"tempChar": ""
},
{
"order": 90,
@@ -1093,7 +1085,7 @@
"name": "icon-summary-widget",
"prevSize": 24,
"code": 60189,
"tempChar": ""
"tempChar": ""
},
{
"order": 92,
@@ -1101,7 +1093,7 @@
"name": "icon-notebook",
"prevSize": 24,
"code": 60190,
"tempChar": ""
"tempChar": ""
},
{
"order": 168,
@@ -1109,7 +1101,7 @@
"name": "icon-tabs-view",
"prevSize": 24,
"code": 60191,
"tempChar": ""
"tempChar": ""
},
{
"order": 117,
@@ -1117,7 +1109,7 @@
"name": "icon-flexible-layout",
"prevSize": 24,
"code": 60192,
"tempChar": ""
"tempChar": ""
},
{
"order": 166,
@@ -1125,7 +1117,7 @@
"name": "icon-generator-sine",
"prevSize": 24,
"code": 60193,
"tempChar": ""
"tempChar": ""
},
{
"order": 167,
@@ -1133,7 +1125,7 @@
"name": "icon-generator-event",
"prevSize": 24,
"code": 60194,
"tempChar": ""
"tempChar": ""
},
{
"order": 165,
@@ -1141,7 +1133,7 @@
"name": "icon-gauge-v2",
"prevSize": 24,
"code": 60195,
"tempChar": ""
"tempChar": ""
},
{
"order": 170,
@@ -1149,7 +1141,7 @@
"name": "icon-spectra",
"prevSize": 24,
"code": 60196,
"tempChar": ""
"tempChar": ""
},
{
"order": 171,
@@ -1157,7 +1149,7 @@
"name": "icon-telemetry-spectra",
"prevSize": 24,
"code": 60197,
"tempChar": ""
"tempChar": ""
},
{
"order": 172,
@@ -1165,7 +1157,7 @@
"name": "icon-pushbutton",
"prevSize": 24,
"code": 60198,
"tempChar": ""
"tempChar": ""
},
{
"order": 174,
@@ -1173,7 +1165,7 @@
"name": "icon-conditional",
"prevSize": 24,
"code": 60199,
"tempChar": ""
"tempChar": ""
},
{
"order": 178,
@@ -1181,7 +1173,7 @@
"name": "icon-condition-widget",
"prevSize": 24,
"code": 60200,
"tempChar": ""
"tempChar": ""
},
{
"order": 180,
@@ -1189,7 +1181,7 @@
"name": "icon-alphanumeric",
"prevSize": 24,
"code": 60201,
"tempChar": ""
"tempChar": ""
},
{
"order": 183,
@@ -1197,7 +1189,7 @@
"name": "icon-image-telemetry",
"prevSize": 24,
"code": 60202,
"tempChar": ""
"tempChar": ""
}
],
"id": 0,
@@ -3001,29 +2993,6 @@
]
}
},
{
"id": 168,
"paths": [
"M896 320v448c-0.215 70.606-57.394 127.785-127.979 128l-0.021 0h-576c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0z",
"M832 704v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0h-192l-101.5-82.74c-24.88-24.9-74.040-45.26-109.24-45.26h-237.26c-35.305 0.102-63.898 28.695-64 63.99l-0 0.010v640c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021zM128 644v-516l256 260z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-folders-collapse"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 97,
"paths": [

View File

@@ -112,7 +112,6 @@
<glyph unicode="&#xea38;" glyph-name="icon-grid-on" d="M1024 448v128h-256v256h-128v-256h-256v256h-128v-256h-256v-128h256v-256h-256v-128h256v-256h128v256h256v-256h128v256h256v128h-256v256zM640 192h-256v256h256z" />
<glyph unicode="&#xea39;" glyph-name="icon-grid-off" d="M256 280.6l128 157.6v9.8h8l104 128h-112v256h-128v-256h-256v-128h256v-167.4zM184 192h-184v-128h80l104 128zM768 359.4l-128-157.6v-9.8h-8l-104-128h112v-256h128v256h256v128h-256v167.4zM840 448h184v128h-80l-104-128zM832 832l-832-1024h192l832 1024h-192z" />
<glyph unicode="&#xea3a;" glyph-name="icon-camera" d="M896 576h-128l-128 256h-256l-128-256h-128c-70.601-0.227-127.773-57.399-128-127.978v-512.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v512.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM512-32c-141.385 0-256 114.615-256 256s114.615 256 256 256c141.385 0 256-114.615 256-256v0c0-141.385-114.615-256-256-256v0z" />
<glyph unicode="&#xea3b;" glyph-name="icon-folders-collapse" d="M896 512v-448c-0.215-70.606-57.394-127.785-127.979-128h-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v448.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM832 128v448c-0.215 70.606-57.394 127.785-127.979 128h-192.021l-101.5 82.74c-24.88 24.9-74.040 45.26-109.24 45.26h-237.26c-35.305-0.102-63.898-28.695-64-63.99v-640.010c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v0.021zM128 188v516l256-260z" />
<glyph unicode="&#xeb00;" glyph-name="icon-activity" d="M576 768h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
<glyph unicode="&#xeb01;" glyph-name="icon-activity-mode" d="M512 832c-214.8 0-398.8-132.4-474.8-320h90.8c56.8 0 108-24.8 143-64h241l-192 192h256l320-320-320-320h-256l192 192h-241c-35-39.2-86.2-64-143-64h-90.8c76-187.6 259.8-320 474.8-320 282.8 0 512 229.2 512 512s-229.2 512-512 512z" />
<glyph unicode="&#xeb02;" glyph-name="icon-autoflow-tabular" d="M192 832c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 832h256v-1024h-256v1024zM832 832h-64v-704h256v512c0 105.6-86.4 192-192 192z" />

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -75,7 +75,7 @@ export default {
event.preventDefault();
this.preview();
} else {
this.openmct.router.navigate(this.objectLink);
window.location.assign(this.objectLink);
}
},
preview() {

View File

@@ -180,16 +180,16 @@ export default {
},
hasParent() {
return this.domainObject !== PLACEHOLDER_OBJECT
&& this.parentUrl !== '/browse';
&& this.parentUrl !== '#/browse';
},
parentUrl() {
const objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
const hash = this.openmct.router.getCurrentLocation().path;
let objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
let hash = window.location.hash;
return hash.slice(0, hash.lastIndexOf('/' + objectKeyString));
},
type() {
const objectType = this.openmct.types.get(this.domainObject.type);
let objectType = this.openmct.types.get(this.domainObject.type);
if (!objectType) {
return {};
}
@@ -336,7 +336,7 @@ export default {
});
},
goToParent() {
this.openmct.router.navigate(this.parentUrl);
window.location.hash = this.parentUrl;
},
updateActionItems(actionItems) {
this.statusBarItems = this.actionCollection.getStatusBarActions();

View File

@@ -54,13 +54,6 @@
label="Browse"
collapsable
>
<button
slot="controls"
class="c-icon-button l-shell__reset-tree-button icon-folders-collapse"
title="Collapse all tree items"
@click="handleTreeReset"
>
</button>
<button
slot="controls"
class="c-icon-button l-shell__sync-tree-button icon-target"
@@ -70,7 +63,6 @@
</button>
<mct-tree
:sync-tree-navigation="triggerSync"
:reset-tree-navigation="triggerReset"
class="l-shell__tree"
/>
</pane>
@@ -156,7 +148,6 @@ export default {
hasToolbar: false,
actionCollection: undefined,
triggerSync: false,
triggerReset: false,
headExpanded
};
},
@@ -237,9 +228,6 @@ export default {
},
handleSyncTreeNavigation() {
this.triggerSync = !this.triggerSync;
},
handleTreeReset() {
this.triggerReset = !this.triggerReset;
}
}
};

View File

@@ -100,14 +100,8 @@
&__pane-tree {
background: linear-gradient(90deg, transparent 70%, rgba(black, 0.2) 99%, rgba(black, 0.3));
.l-pane__header {
// Hide all buttons except the collapse button
> :not(.l-pane__collapse-button) {
display: none;
}
}
[class*="expand-button"] {
[class*="expand-button"],
[class*="sync-tree-button"] {
display: none;
}

View File

@@ -30,7 +30,7 @@
width: 100%;
}
&__scrollable {
&__scrollable-children {
overflow: auto;
}
@@ -56,7 +56,6 @@
@include userSelectNone();
overflow-x: hidden;
overflow-y: auto;
padding-right: $interiorMarginSm;
.icon-arrow-nav-to-parent {
visibility: hidden;
@@ -91,7 +90,7 @@
color: $colorItemTreeIcon;
}
@include hover {
&:hover {
background: $colorItemTreeHoverBg;
filter: $filterHov;
}
@@ -125,16 +124,14 @@
}
&__item {
[class*="view-control"] {
padding: 2px 10px;
}
> * + * {
margin-left: ceil($interiorMarginSm / 2);
margin-left: $interiorMarginSm;
}
@include hover {
background: $colorItemTreeHoverBg;
@include desktop {
&:hover {
background: $colorItemTreeHoverBg;
}
}
// Object labels in trees

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,9 @@
<template>
<div
:style="treeItemStyles"
:style="{
'top': virtualScroll ? itemTop : 'auto',
'position': virtualScroll ? 'absolute' : 'relative'
}"
class="c-tree__item-h"
>
<div
@@ -14,23 +17,28 @@
@contextmenu.capture="handleContextMenu"
>
<view-control
ref="navigate"
ref="navUp"
v-model="expanded"
class="c-tree__item__view-control"
:value="isOpen || isLoading"
:enabled="!activeSearch && hasComposition"
@input="navigationClick()"
:control-class="'icon-arrow-nav-to-parent'"
:enabled="showUp"
@input="resetTreeHere"
/>
<object-label
ref="objectLabel"
:domain-object="node.object"
:object-path="node.objectPath"
:navigate-to-path="navigationPath"
:style="{ paddingLeft: leftOffset }"
@context-click-active="setContextClickActive"
/>
<span
v-if="isLoading"
class="loading"
></span>
<view-control
ref="navDown"
v-model="expanded"
class="c-tree__item__view-control"
:control-class="'c-nav__down'"
:enabled="hasComposition && showDown"
/>
</div>
</div>
</template>
@@ -51,14 +59,18 @@ export default {
type: Object,
required: true
},
activeSearch: {
type: Boolean,
default: false
},
leftOffset: {
type: String,
default: '0px'
},
showUp: {
type: Boolean,
default: false
},
showDown: {
type: Boolean,
default: true
},
itemIndex: {
type: Number,
required: false,
@@ -74,13 +86,9 @@ export default {
required: false,
default: 0
},
openItems: {
type: Array,
required: true
},
loadingItems: {
type: Object,
required: true
virtualScroll: {
type: Boolean,
default: false
}
},
data() {
@@ -89,6 +97,7 @@ export default {
return {
hasComposition: false,
navigated: this.isNavigated(),
expanded: false,
contextClickActive: false
};
},
@@ -104,44 +113,37 @@ export default {
return parentKeyString !== this.node.object.location;
},
isLoading() {
return Boolean(this.loadingItems[this.navigationPath]);
},
isOpen() {
return this.openItems.includes(this.navigationPath);
},
treeItemStyles() {
let itemTop = (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
return {
'top': itemTop,
'position': 'absolute',
'padding-left': this.leftOffset
};
itemTop() {
return (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
}
},
watch: {
expanded() {
this.$emit('expanded', this.domainObject);
}
},
mounted() {
this.domainObject = this.node.object;
let objectComposition = this.openmct.composition.get(this.node.object);
if (this.openmct.composition.get(this.domainObject)) {
this.domainObject = this.node.object;
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
this.domainObject = newObject;
});
this.$once('hook:destroyed', removeListener);
if (objectComposition) {
this.hasComposition = true;
}
this.openmct.router.on('change:path', this.highlightIfNavigated);
this.$emit('tree-item-mounted', this.navigationPath);
},
destroyed() {
this.openmct.router.off('change:path', this.highlightIfNavigated);
this.$emit('tree-item-destoyed', this.navigationPath);
},
methods: {
navigationClick() {
this.$emit('navigation-click', this.isOpen || this.isLoading ? 'close' : 'open');
},
handleClick(event) {
// skip for navigation, let viewControl handle click
if (this.$refs.navigate.$el === event.target) {
if ([this.$refs.navUp.$el, this.$refs.navDown.$el].includes(event.target)) {
return;
}
@@ -158,6 +160,9 @@ export default {
highlightIfNavigated() {
this.navigated = this.isNavigated();
},
resetTreeHere() {
this.$emit('resetTree', this.node);
},
setContextClickActive(active) {
this.contextClickActive = active;
}

View File

@@ -23,382 +23,7 @@
const LocationBar = require('location-bar');
const EventEmitter = require('EventEmitter');
const _ = require('lodash');
class ApplicationRouter extends EventEmitter {
/**
* events
* change:params -> notify listeners w/ new, old, and changed.
* change:path -> notify listeners w/ new, old paths.
*
* methods:
* update(path, params) -> updates path and params at the same time. Only
* updates specified params, other params are not modified.
* updateParams(newParams) -> update only specified params, leaving rest
* intact. Does not modify path.
* updatePath(path);
* route(path, handler);
* start(); Start routing.
*/
constructor(openmct) {
super();
this.locationBar = new LocationBar();
this.openmct = openmct;
this.routes = [];
this.started = false;
this.setHash = _.debounce(this.setHash.bind(this), 300);
}
// Public Methods
destroy() {
this.locationBar.stop();
}
/**
* Delete a given query parameter from current url
*
* @param {string} paramName name of searchParam to delete from current url searchParams
*/
deleteSearchParam(paramName) {
let url = this.getHashRelativeURL();
url.searchParams.delete(paramName);
this.setLocationFromUrl();
}
/**
* object for accessing all current search parameters
*
* @returns {URLSearchParams} A {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/entries|URLSearchParams}
*/
getAllSearchParams() {
return this.getHashRelativeURL().searchParams;
}
/**
* Uniquely identifies a domain object.
*
* @typedef CurrentLocation
* @property {URL} url current url location
* @property {string} path current url location pathname
* @property {string} getQueryString a function which returns url search query
* @property {object} params object representing url searchParams
*/
/**
* object for accessing current url location and search params
*
* @returns {CurrentLocation} A {@link CurrentLocation}
*/
getCurrentLocation() {
return this.currentLocation;
}
/**
* Get current location URL Object
*
* @returns {URL} current url location
*/
getHashRelativeURL() {
return this.getCurrentLocation().url;
}
/**
* Get current location URL Object searchParams
*
* @returns {object} object representing current url searchParams
*/
getParams() {
return this.currentLocation.params;
}
/**
* Get a value of given param from current url searchParams
*
* @returns {string} value of paramName from current url searchParams
*/
getSearchParam(paramName) {
return this.getAllSearchParams().get(paramName);
}
/**
* Navgate to given hash and update current location object and notify listeners about location change
*
* @param {string} paramName name of searchParam to get from current url searchParams
*
* @returns {string} value of paramName from current url searchParams
*/
navigate(hash) {
this.handleLocationChange(hash.substring(1));
}
/**
* Add routes listeners
*
* @param {string} matcher Regex to match value in url
* @param {@function} callback function called when found match in url
*/
route(matcher, callback) {
this.routes.push({
matcher,
callback
});
}
/**
* Set url hash using path and queryString
*
* @param {string} path path for url
* @param {string} queryString queryString for url
*/
set(path, queryString) {
this.setHash(`${path}?${queryString}`);
}
/**
* Will replace all current search parameters with the ones defined in urlSearchParams
*/
setAllSearchParams() {
this.setLocationFromUrl();
}
/**
* To force update url based on value in currentLocation object
*/
setLocationFromUrl() {
this.updateTimeSettings();
}
/**
* Set url hash using path
*
* @param {string} path path for url
*/
setPath(path) {
this.handleLocationChange(path.substring(1));
}
/**
* Update param value from current url searchParams
*
* @param {string} paramName param name from current url searchParams
* @param {string} paramValue param value from current url searchParams
*/
setSearchParam(paramName, paramValue) {
let url = this.getHashRelativeURL();
url.searchParams.set(paramName, paramValue);
this.setLocationFromUrl();
}
/**
* start application routing, should be done after handlers are registered.
*/
start() {
if (this.started) {
throw new Error('Router already started!');
}
this.started = true;
this.locationBar.onChange(p => this.hashChaged(p));
this.locationBar.start({
root: location.pathname
});
}
/**
* Set url hash using path and searchParams object
*
* @param {string} path path for url
* @param {string} params oject representing searchParams key/value
*/
update(path, params) {
let searchParams = this.currentLocation.url.searchParams;
for (let [key, value] of Object.entries(params)) {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
}
this.set(path, searchParams.toString());
}
/**
* Update route params. Takes an object of updates. New parameters
*/
updateParams(updateParams) {
let searchParams = this.currentLocation.url.searchParams;
Object.entries(updateParams).forEach(([key, value]) => {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
});
this.setQueryString(searchParams.toString());
}
/**
* To force update url based on value in currentLocation object
*/
updateTimeSettings() {
const hash = `${this.currentLocation.path}?${this.currentLocation.getQueryString()}`;
this.setHash(hash);
}
// Private Methods
/**
* @private
* Create currentLocation object
*
* @param {string} pathString USVString representing relative URL.
*
* @returns {CurrentLocation} A {@link CurrentLocation}
*/
createLocation(pathString) {
if (pathString[0] !== '/') {
pathString = '/' + pathString;
}
let url = new URL(
pathString,
`${location.protocol}//${location.host}${location.pathname}`
);
return {
url: url,
path: url.pathname,
getQueryString: () => url.search.replace(/^\?/, ''),
params: paramsToObject(url.searchParams)
};
}
/**
* @private
* Compare new and old path and on change emit event 'change:path'
*
* @param {string} newPath new path of url
* @param {string} oldPath old path of url
*/
doPathChange(newPath, oldPath) {
if (newPath === oldPath) {
return;
}
let route = this.routes.filter(r => r.matcher.test(newPath))[0];
if (route) {
route.callback(newPath, route.matcher.exec(newPath), this.currentLocation.params);
}
this.emit('change:path', newPath, oldPath);
}
/**
* @private
* Compare new and old params and on change emit event 'change:params'
*
* @param {object} newParams new params of url
* @param {object} oldParams old params of url
*/
doParamsChange(newParams, oldParams) {
if (_.isEqual(newParams, oldParams)) {
return;
}
let changedParams = {};
Object.entries(newParams).forEach(([key, value]) => {
if (value !== oldParams[key]) {
changedParams[key] = value;
}
});
Object.keys(oldParams).forEach(key => {
if (!Object.prototype.hasOwnProperty.call(newParams, key)) {
changedParams[key] = undefined;
}
});
this.emit('change:params', newParams, oldParams, changedParams);
}
/**
* @private
* On location change, update currentLocation object and emit appropriate events
*
* @param {string} pathString USVString representing relative URL.
*/
handleLocationChange(pathString) {
let oldLocation = this.currentLocation;
let newLocation = this.createLocation(pathString);
this.currentLocation = newLocation;
if (!oldLocation) {
this.doPathChange(newLocation.path, null);
this.doParamsChange(newLocation.params, {});
return;
}
this.doPathChange(
newLocation.path,
oldLocation.path
);
this.doParamsChange(
newLocation.params,
oldLocation.params
);
}
/**
* @private
* On hash changed, update currentLocation object and emit appropriate events
*
* @param {string} hash new hash for url
*/
hashChaged(hash) {
this.emit('change:hash', hash);
this.handleLocationChange(hash);
}
/**
* @private
* Set new hash for url
*
* @param {string} hash new hash for url
*/
setHash(hash) {
location.hash = '#' + hash.replace(/#/g, '');
}
/**
* @private
* Set queryString part of current url
*
* @param {string} queryString queryString part of url
*/
setQueryString(queryString) {
this.handleLocationChange(`${this.currentLocation.path}?${queryString}`);
}
}
/**
* Convert searchParams into Object
*
* @param {URLSearchParams} searchParams queryString part of url
*
* @returns {Object}
*/
function paramsToObject(searchParams) {
let params = {};
for (let [key, value] of searchParams.entries()) {
@@ -416,4 +41,169 @@ function paramsToObject(searchParams) {
return params;
}
class ApplicationRouter extends EventEmitter {
/**
* events
* change:params -> notify listeners w/ new, old, and changed.
* change:path -> notify listeners w/ new, old paths.
*
* methods:
* update(path, params) -> updates path and params at the same time. Only
* updates specified params, other params are not modified.
* updateParams(newParams) -> update only specified params, leaving rest
* intact. Does not modify path.
* updatePath(path);
* route(path, handler);
* start(); Start routing.
*/
constructor() {
super();
this.routes = [];
this.started = false;
this.locationBar = new LocationBar();
}
/**
* start application routing, should be done after handlers are registered.
*/
start() {
if (this.started) {
throw new Error('Router already started!');
}
this.started = true;
this.locationBar.onChange(p => this.handleLocationChange(p));
this.locationBar.start({
root: location.pathname
});
}
destroy() {
this.locationBar.stop();
this.removeAllListeners();
}
handleLocationChange(pathString) {
if (pathString[0] !== '/') {
pathString = '/' + pathString;
}
let url = new URL(
pathString,
`${location.protocol}//${location.host}${location.pathname}`
);
let oldLocation = this.currentLocation;
let newLocation = {
url: url,
path: url.pathname,
queryString: url.search.replace(/^\?/, ''),
params: paramsToObject(url.searchParams)
};
this.currentLocation = newLocation;
if (!oldLocation) {
this.doPathChange(newLocation.path, null, newLocation);
this.doParamsChange(newLocation.params, {}, newLocation);
return;
}
if (oldLocation.path !== newLocation.path) {
this.doPathChange(
newLocation.path,
oldLocation.path,
this
);
}
if (!_.isEqual(oldLocation.params, newLocation.params)) {
this.doParamsChange(
newLocation.params,
oldLocation.params,
newLocation
);
}
}
doPathChange(newPath, oldPath, newLocation) {
let route = this.routes.filter(r => r.matcher.test(newPath))[0];
if (route) {
route.callback(newPath, route.matcher.exec(newPath), this.currentLocation.params);
}
this.emit('change:path', newPath, oldPath);
}
doParamsChange(newParams, oldParams, newLocation) {
let changedParams = {};
Object.entries(newParams).forEach(([key, value]) => {
if (value !== oldParams[key]) {
changedParams[key] = value;
}
});
Object.keys(oldParams).forEach(key => {
if (!Object.prototype.hasOwnProperty.call(newParams, key)) {
changedParams[key] = undefined;
}
});
this.emit('change:params', newParams, oldParams, changedParams);
}
/**
* Update route params. Takes an object of updates. New parameters
*/
updateParams(updateParams) {
let searchParams = this.currentLocation.url.searchParams;
Object.entries(updateParams).forEach(([key, value]) => {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
});
this.setQueryString(searchParams.toString());
}
getParams() {
return this.currentLocation.params;
}
update(path, params) {
let searchParams = this.currentLocation.url.searchParams;
for (let [key, value] of Object.entries(params)) {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
}
this.set(path, searchParams.toString());
}
set(path, queryString) {
location.hash = `${path}?${queryString}`;
}
setQueryString(queryString) {
this.set(this.currentLocation.path, queryString);
}
setPath(path) {
this.set(path, this.currentLocation.queryString);
}
route(matcher, callback) {
this.routes.push({
matcher,
callback
});
}
}
module.exports = ApplicationRouter;

View File

@@ -1,139 +0,0 @@
import { createOpenMct, resetApplicationState } from 'utils/testing';
let openmct;
let element;
let child;
let appHolder;
let resolveFunction;
let initialHash = '';
describe('Application router utility functions', () => {
beforeAll(done => {
appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalTimeSystem());
openmct.install(openmct.plugins.UTCTimeSystem());
element = document.createElement('div');
child = document.createElement('div');
element.appendChild(child);
openmct.on('start', done);
openmct.start(appHolder);
document.body.append(appHolder);
});
afterAll(() => {
openmct.router.setHash(initialHash);
appHolder.remove();
return resetApplicationState(openmct);
});
it('has initial hash when loaded', (done) => {
let success;
resolveFunction = () => {
openmct.router.setLocationFromUrl();
success = window.location.hash !== null;
if (success) {
initialHash = window.location.hash;
expect(success).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
it('The setSearchParam function sets an individual search parameter in the window location hash', (done) => {
let success;
openmct.router.setSearchParam('testParam', 'testValue');
resolveFunction = () => {
success = window.location.hash.includes('testParam=testValue');
if (success) {
expect(success).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
it('The getSearchParam function returns the value of an individual search paramater in the window location hash', () => {
expect(openmct.router.getSearchParam('testParam')).toBe('testValue');
});
it('The deleteSearchParam function deletes an individual search paramater in the window location hash', (done) => {
let success;
openmct.router.deleteSearchParam('testParam');
resolveFunction = () => {
success = window.location.hash.includes('testParam=testValue') === false;
if (success) {
expect(success).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
it('The setSearchParam function sets an individual search parameters in the window location hash', (done) => {
let success;
openmct.router.setSearchParam('testParam1', 'testValue1');
openmct.router.setSearchParam('testParam2', 'testValue2');
resolveFunction = () => {
const hasTestParam1 = window.location.hash.includes('testParam1=testValue1');
const hasTestParam2 = window.location.hash.includes('testParam2=testValue2');
success = hasTestParam1 && hasTestParam2;
if (success) {
expect(success).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
it('The setAllSearchParams function replaces all search paramaters in the window location hash', (done) => {
let success;
openmct.router.setSearchParam('testParam2', 'updatedtestValue2');
openmct.router.setSearchParam('newTestParam3', 'newTestValue3');
resolveFunction = () => {
const hasupdatedValueForTestParam2 = window.location.hash.includes('testParam2=updatedtestValue2');
const hasNewTestParam3 = window.location.hash.includes('newTestParam3=newTestValue3');
success = hasupdatedValueForTestParam2 && hasNewTestParam3;
if (success) {
expect(success).toBe(true);
openmct.router.removeListener('change:hash', resolveFunction);
done();
}
};
openmct.router.on('change:hash', resolveFunction);
});
it('The getAllSearchParams function returns the values of all search paramaters in the window location hash', () => {
let searchParams = openmct.router.getAllSearchParams();
expect(searchParams.get('testParam1')).toBe('testValue1');
expect(searchParams.get('testParam2')).toBe('updatedtestValue2');
expect(searchParams.get('newTestParam3')).toBe('newTestValue3');
});
});

View File

@@ -13,12 +13,13 @@ define([
let mutable;
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
isRoutingInProgress = true;
let navigatePath = results[1];
clearMutationListeners();
navigateToPath(navigatePath, params.view);
onParamsChanged(null, null, params);
});
openmct.router.on('change:params', onParamsChanged);
@@ -132,21 +133,18 @@ define([
}
function navigateToFirstChildOfRoot() {
openmct.objects.get('ROOT')
.then(rootObject => {
openmct.composition.get(rootObject).load()
.then(children => {
let lastChild = children[children.length - 1];
if (!lastChild) {
console.error('Unable to navigate to anything. No root objects found.');
} else {
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
openmct.router.setPath(`#/browse/${lastChildId}`);
}
})
.catch(e => console.error(e));
})
.catch(e => console.error(e));
openmct.objects.get('ROOT').then(rootObject => {
openmct.composition.get(rootObject).load()
.then(children => {
let lastChild = children[children.length - 1];
if (!lastChild) {
console.error('Unable to navigate to anything. No root objects found.');
} else {
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
openmct.router.setPath(`#/browse/${lastChildId}`);
}
});
});
}
function clearMutationListeners() {

View File

@@ -0,0 +1,108 @@
/*****************************************************************************
* 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 objectUtils from '../api/objects/object-utils.js';
/**
* Utility functions for getting and setting Open MCT search parameters and navigated object path.
* Open MCT encodes application state into the "hash" of the url, making it awkward to use standard browser API such
* as URL for modifying state in the URL. This wraps native API with some utility functions that operate only on the
* hash section of the URL.
*/
export function setSearchParam(paramName, paramValue) {
let url = getHashRelativeURL();
url.searchParams.set(paramName, paramValue);
setLocationFromUrl(url);
}
export function deleteSearchParam(paramName) {
let url = getHashRelativeURL();
url.searchParams.delete(paramName);
setLocationFromUrl(url);
}
/**
* Will replace all current search parameters with the ones defined in urlSearchParams
* @param {URLSearchParams} paramMap
*/
export function setAllSearchParams(newSearchParams) {
let url = getHashRelativeURL();
Array.from(url.searchParams.keys()).forEach((key) => url.searchParams.delete(key));
Array.from(newSearchParams.keys()).forEach(key => {
url.searchParams.set(key, newSearchParams.get(key));
});
setLocationFromUrl(url);
}
export function getSearchParam(paramName) {
return getAllSearchParams().get(paramName);
}
/**
* @returns {URLSearchParams} A {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/entries|URLSearchParams}
* object for accessing all current search parameters
*/
export function getAllSearchParams() {
return getHashRelativeURL().searchParams;
}
export function getObjectPath() {
return getHashRelativeURL().pathname;
}
export function setObjectPath(objectPath) {
let objectPathString;
let url = getHashRelativeURL();
if (objectPath instanceof Array) {
if (objectPath.length > 0 && isDomainObject(objectPath[0])) {
throw 'setObjectPath must be called with either a string, or an array of Domain Objects';
}
objectPathString = objectPath.reduce((pathString, object) => {
return `${pathString}/${objectUtils.makeKeyString(object.identifier)}`;
}, '');
} else {
objectPathString = objectPath;
}
url.pathname = objectPathString;
setLocationFromUrl(url);
}
function isDomainObject(potentialObject) {
return potentialObject.identifier === undefined;
}
function setLocationFromUrl(url) {
window.location.hash = `${url.pathname}${url.search}`;
}
function getHashRelativeURL() {
return new URL(window.location.hash.substring(1), window.location.origin);
}

View File

@@ -0,0 +1,113 @@
/*****************************************************************************
* 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 {
setSearchParam,
deleteSearchParam,
getAllSearchParams,
getSearchParam,
setAllSearchParams,
getObjectPath,
setObjectPath
} from './openmctLocation';
import {resetApplicationState} from 'utils/testing';
describe('the openmct location utility functions', () => {
afterEach(() => resetApplicationState());
it('The setSearchParam function sets an individual search parameters in the window location hash', () => {
setSearchParam('testParam', 'testValue');
expect(window.location.hash.includes('testParam=testValue')).toBe(true);
});
it('The deleteSearchParam function deletes an individual search paramater in the window location hash', () => {
window.location.hash = '#/?testParam=testValue';
deleteSearchParam('testParam');
expect(window.location.hash.includes('testParam=testValue')).toBe(false);
});
it('The getSearchParam function returns the value of an individual search paramater in the window location hash', () => {
window.location.hash = '#/?testParam=testValue';
expect(getSearchParam('testParam')).toBe('testValue');
});
it('The getAllSearchParams function returns the values of all search paramaters in the window location hash', () => {
window.location.hash = '#/?testParam1=testValue1&testParam2=testValue2&testParam3=testValue3';
let searchParams = getAllSearchParams();
expect(searchParams.get('testParam1')).toBe('testValue1');
expect(searchParams.get('testParam2')).toBe('testValue2');
expect(searchParams.get('testParam3')).toBe('testValue3');
});
it('The setAllSearchParams function replaces all search paramaters in the window location hash', () => {
window.location.hash = '#/?testParam1=testValue1&testParam2=testValue2&testParam3=testValue3';
let searchParams = getAllSearchParams();
searchParams.delete('testParam3');
searchParams.set('testParam1', 'updatedTestValue1');
searchParams.set('newTestParam4', 'newTestValue4');
setAllSearchParams(searchParams);
expect(window.location.hash).toBe('#/?testParam1=updatedTestValue1&testParam2=testValue2&newTestParam4=newTestValue4');
});
it('The getObjectPath function returns the current object path', () => {
window.location.hash = '#/some/object/path?someParameter=someValue';
expect(getObjectPath()).toBe('/some/object/path');
});
it('The setObjectPath function allows the object path to be set to a given string', () => {
window.location.hash = '#/some/object/path?someParameter=someValue';
setObjectPath('/some/other/object/path');
expect(window.location.hash).toBe('#/some/other/object/path?someParameter=someValue');
});
it('The setObjectPath function allows the object path to be set from an array of domain objects', () => {
const OBJECT_PATH = [
{
identifier: {
namespace: 'namespace',
key: 'objectKey1'
}
},
{
identifier: {
namespace: 'namespace',
key: 'objectKey2'
}
},
{
identifier: {
namespace: 'namespace',
key: 'objectKey3'
}
}
];
window.location.hash = '#/some/object/path?someParameter=someValue';
setObjectPath(OBJECT_PATH);
expect(window.location.hash).toBe('#/namespace:objectKey1/namespace:objectKey2/namespace:objectKey3?someParameter=someValue');
});
it('The setObjectPath function throws an error if called with anything other than a string or an array of domain objects', () => {
expect(() => setObjectPath(["array", "of", "strings"])).toThrow();
expect(() => setObjectPath([{}, {someKey: 'someValue'}])).toThrow();
});
});