[Plots] #638 added onchange handling in order to synchronize forms with domain object model.

Fixed failing test

Added tests

jslint errors

Minor refactoring of layout bundle

revert layout/bundle.json
This commit is contained in:
Henry 2016-02-03 16:00:11 -08:00
parent f2903f4030
commit abf5f22155
24 changed files with 867 additions and 190 deletions

View File

@ -295,7 +295,7 @@ define([
"provides": "typeService",
"type": "decorator",
"implementation": TypeRegionDecorator
},
}
],
"runs": [
{

View File

@ -62,7 +62,7 @@ define(
}
};
this.addPart(metadataPart, 0);
}
};
return InspectorRegion;
}

View File

@ -29,16 +29,17 @@ define(
"use strict";
/**
* Adds default screen regions to Type definitions. Screen regions
* are sections of the browse and edit view of an object that can be
* customized on a per-type basis. Within {@link Region}s are {@link RegionPart}s.
* Policies can be used to decide which parts are visible or not based on object state.
* Adds default browse screen regions to Type definitions. Screen
* regions are sections of the browse and edit view of an object
* that can be customized on a per-type basis. Within
* {@link Region}s are {@link RegionPart}s. Policies can be used to
* decide which parts are visible or not based on object state.
* @memberOf platform/commonUI/regions
* @see {@link Region}, {@link RegionPart}, {@link EditableRegionPolicy}
* @constructor
*/
function TypeRegionDecorator(typeService) {
this.typeService = typeService
this.typeService = typeService;
}
/**

View File

@ -33,11 +33,11 @@ define(
var inspectorRegion;
beforeEach(function () {
inspectorRegion = new InspectorRegion;
inspectorRegion = new InspectorRegion();
});
it("creates default region parts", function () {
expect(inspectorRegion.parts().length).toBe(2);
expect(inspectorRegion.parts.length).toBe(1);
});
});

View File

@ -0,0 +1,70 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* MCTIncudeSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/TypeRegionDecorator"],
function (TypeRegionDecorator) {
"use strict";
describe("The type region decorator", function () {
var typeRegionDecorator,
mockTypeService,
mockType,
mockTypeDefinition;
beforeEach(function () {
mockTypeDefinition = {};
mockType = jasmine.createSpyObj('type', [
'getDefinition'
]);
mockType.getDefinition.andReturn(mockTypeDefinition);
mockTypeService = jasmine.createSpyObj('typeService', [
'listTypes',
'getType'
]);
mockTypeService.getType.andReturn(mockType);
mockTypeService.listTypes.andReturn([mockType]);
typeRegionDecorator = new TypeRegionDecorator(mockTypeService);
});
it("decorates individual type definitions with basic inspector" +
" region", function () {
typeRegionDecorator.getType('someType');
expect(mockTypeDefinition.regions).toBeDefined();
});
it("decorates all type definitions with basic inspector" +
" region", function () {
typeRegionDecorator.listTypes();
expect(mockTypeDefinition.regions).toBeDefined();
});
});
}
);

View File

@ -23,6 +23,7 @@
ng-controller="DateTimeFieldController">
<input type="text"
ng-model="textValue"
ng-change="structure.onchange(ngModel[field])"
ng-blur="restoreTextValue(); ngBlur()"
ng-class="{
error: textInvalid ||

View File

@ -44,11 +44,11 @@ define(
if (domainObject.getCapability('status').get('editing')){
//If the domain object is in edit mode, only include a part
// if it is marked editable
return regionPart.modes.indexOf('edit') != -1;
return regionPart.modes.indexOf('edit') !== -1;
} else {
//If the domain object is not in edit mode, return any parts
// that are not explicitly marked editable
return regionPart.modes.indexOf('browse') != -1;
return regionPart.modes.indexOf('browse') !== -1;
}
};

View File

@ -74,17 +74,19 @@ define(
/**
* Removes a part from this region.
* @param {RegionPart} part the part to add
* @param {number} [index] the position to insert the part. By default
* will add to the end
* @param {RegionPart | number | strnig} part The region part to
* remove. If a number, will remove the part at that index. If a
* string, will remove the part with the matching name. If an
* object, will attempt to remove that object from the Region
*/
Region.prototype.removePart = function (part){
if (typeof part === 'number') {
this.parts.splice(part, 1);
} else if (typeof part === 'string'){
this.parts
}
else {
this.parts = this.parts.filter(function(thisPart) {
return thisPart.name !== part;
});
} else {
this.parts.splice(this.parts.indexOf(part), 1);
}
};

View File

@ -37,22 +37,19 @@ define(
typeCapability = domainObject.getCapability('type');
/**
* TODO: Refactor this out, probably to a directive.
* Or, alternatively, could have a regionCapability that returns
* regions and parts filtered for applicability to current
* object state.
* Filters region parts to only those allowed by region policies
* @param regions
* @returns {*}
* @returns {{}}
*/
function filterParts(regions) {
//Dupe so we're not modifying the type definition.
var filteredRegions = {};
for (var regionName in regions) {
Object.keys(regions).forEach(function(regionName) {
filteredRegions[regionName] = Object.create(regions[regionName]);
filteredRegions[regionName].parts = regions[regionName].parts.filter(function(part){
filteredRegions[regionName].parts = (regions[regionName].parts || []).filter(function(part){
return policyService.allow('region', part, domainObject);
});
}
});
return filteredRegions;
}

View File

@ -0,0 +1,75 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
define(
['../src/EditableRegionPolicy'],
function (EditableRegionPolicy) {
"use strict";
describe("The editable region policy ", function () {
var editableRegionPolicy,
mockDomainObject,
mockStatusCapability,
mockBrowseRegionPart = {
modes: 'browse'
},
mockEditRegionPart = {
modes: 'edit'
},
mockAllModesRegionPart = {};
beforeEach(function(){
editableRegionPolicy = new EditableRegionPolicy();
mockStatusCapability = jasmine.createSpyObj("statusCapability", [
"get"
]);
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getCapability"
]);
mockDomainObject.getCapability.andReturn(mockStatusCapability);
});
it("includes only browse region parts for object not in edit mode", function() {
mockStatusCapability.get.andReturn(false);
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
});
it("includes only edit region parts for object in edit mode", function() {
mockStatusCapability.get.andReturn(true);
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
});
it("includes region parts with no mode specification", function() {
mockStatusCapability.get.andReturn(false);
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
mockStatusCapability.get.andReturn(true);
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
});
});
}
);

View File

@ -0,0 +1,82 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
define(
['../src/RegionController'],
function (RegionController) {
"use strict";
describe("The region controller ", function () {
var mockScope,
mockDomainObject,
mockTypeCapability,
mockTypeDefinition,
mockPolicyService,
controller;
beforeEach(function(){
mockTypeDefinition = {
regions:
{
'regionOne': {
'parts': [
{'name': 'Part One'},
{'name': 'Part Two'}
]
}
}
};
mockTypeCapability = jasmine.createSpyObj('typeCapability', [
'getDefinition'
]);
mockTypeCapability.getDefinition.andReturn(mockTypeDefinition);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getCapability'
]);
mockDomainObject.getCapability.andReturn(mockTypeCapability);
mockPolicyService = jasmine.createSpyObj('policyService', [
'allow'
]);
mockScope = {
domainObject: mockDomainObject
};
});
it("filters out regions disallowed by region policy", function() {
mockPolicyService.allow.andReturn(false);
controller = new RegionController(mockScope, mockPolicyService);
expect(mockScope.regions.regionOne.parts.length).toBe(0);
});
it("does not filter out regions allowed by region policy", function() {
mockPolicyService.allow.andReturn(true);
controller = new RegionController(mockScope, mockPolicyService);
expect(mockScope.regions.regionOne.parts.length).toBe(2);
});
});
}
);

View File

@ -0,0 +1,107 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
define(
['../src/Region'],
function (Region) {
"use strict";
describe("The region class ", function () {
var region,
part2 = {'name': 'part2'};
beforeEach(function(){
region = new Region();
region.parts = [
{name: 'part1'},
{name: 'part3'},
{name: 'part4'}
];
});
it("adding a region part at a specified index adds it in that" +
" position", function() {
region.addPart(part2, 1);
expect(region.parts.length).toBe(4);
expect(region.parts[1]).toBe(part2);
});
it("adding a region part without an index adds it at the end", function() {
var partN = {'name': 'partN'};
region.addPart(partN);
expect(region.parts.length).toBe(4);
expect(region.parts[region.parts.length-1]).toBe(partN);
});
describe("removing a region part", function(){
var partName = "part2";
beforeEach(function(){
region.parts = [
{name: 'part1'},
part2,
{name: 'part3'},
{name: 'part4'}
];
});
it("with a string matches on region part" +
" name", function() {
expect(region.parts.length).toBe(4);
expect(region.parts.indexOf(part2)).toBe(1);
region.removePart(partName);
expect(region.parts.length).toBe(3);
expect(region.parts.indexOf(part2)).toBe(-1);
});
it("with a number removes by index", function() {
expect(region.parts.length).toBe(4);
expect(region.parts.indexOf(part2)).toBe(1);
region.removePart(1);
expect(region.parts.length).toBe(3);
expect(region.parts.indexOf(part2)).toBe(-1);
});
it("with object matches that object", function() {
expect(region.parts.length).toBe(4);
expect(region.parts.indexOf(part2)).toBe(1);
region.removePart(part2);
expect(region.parts.length).toBe(3);
expect(region.parts.indexOf(part2)).toBe(-1);
});
});
});
}
);

View File

@ -1,3 +0,0 @@
[
"InspectorRegion"
]

View File

@ -25,45 +25,15 @@ define([
"./src/LayoutController",
"./src/FixedController",
"./src/LayoutCompositionPolicy",
"../../commonUI/browse/src/InspectorRegion",
"./src/PlotOptionsController",
'legacyRegistry'
], function (
LayoutController,
FixedController,
LayoutCompositionPolicy,
InspectorRegion,
PlotOptionsController,
legacyRegistry
) {
"use strict";
/**
* Customize and extend the default 'Inspector' region for the panel
* type, to add display options for plots. This should be moved to a
* dedicated type.
* @type {InspectorRegion}
*/
var plotInspector = new InspectorRegion(),
plotOptionsBrowsePart = {
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
},
plotOptionsEditPart = {
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-browse"
}
};
plotInspector.addPart(plotOptionsBrowsePart);
plotInspector.addPart(plotOptionsEditPart);
legacyRegistry.register("platform/features/layout", {
"name": "Layout components.",
"description": "Plug in adding Layout capabilities.",
@ -222,10 +192,6 @@ define([
{
"key": "frame",
"templateUrl": "templates/frame.html"
},
{
"key": "plot-options-browse",
"templateUrl": "templates/plot-options-browse.html"
}
],
"controllers": [
@ -247,13 +213,6 @@ define([
"telemetryFormatter",
"throttle"
]
},
{
"key": "PlotOptionsController",
"implementation": PlotOptionsController,
"depends": [
"$scope"
]
}
],
"templates": [
@ -353,12 +312,7 @@ define([
"property": "layoutGrid",
"conversion": "number[]"
}
],
"regions": {
"inspector": plotInspector
}
]
}
]
}

View File

@ -19,8 +19,31 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<style>
.l-inspect .l-inspector-part .no-margin .form {
margin-left: 0;
}
.reduced-min-width .form .form-row > .label {
min-width: 80px;
}
</style>
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
<em class="t-inspector-part-header" title="Display properties for this object">Display</em>
<mct-form
ng-model="configuration.plot.xAxis"
structure="xAxisForm"
name="xAxisFormState"
class="flex-elem l-flex-row no-validate no-margin reduced-min-width">
</mct-form>
<mct-form
ng-model="configuration.plot.yAxis"
structure="yAxisForm"
name="yAxisFormState"
class="flex-elem l-flex-row no-validate no-margin reduced-min-width">
</mct-form>
<div class="section-header ng-binding ng-scope">
Plot Series
</div>
<ul class="first flex-elem grows vscroll">
<ul class="tree">
<li ng-repeat="child in children">
@ -41,8 +64,8 @@
</span>
<mct-form
ng-class="{hidden: !toggle.isActive()}"
ng-model="plotOptionsModel"
structure="plotOptionsStructure"
ng-model="configuration.plot.series[child.getId()]"
structure="plotSeriesForm"
name="plotOptionsState"
class="flex-elem l-flex-row l-controls-first no-validate">
</mct-form>

View File

@ -27,8 +27,8 @@
* @namespace platform/features/layout
*/
define(
[],
function () {
['./PlotOptionsForm'],
function (PlotOptionsForm) {
"use strict";
/**
@ -50,136 +50,98 @@ define(
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function PlotOptionsController($scope) {
function PlotOptionsController($scope, topic) {
var self = this,
domainObject = $scope.domainObject,
xAxisForm = {
'name':'x-axis',
'sections': [{
'name': 'x-axis',
'rows': [
{
'name': 'Domain',
'control': 'select',
'key': 'key',
//TODO fetch options from platform or object type configuration
'options': [
{'name':'scet', 'value': 'scet'},
{'name':'sclk', 'value': 'sclk'},
{'name':'lst', 'value': 'lst'}
]
composition,
mutationListener,
formListener,
configuration = domainObject.getModel().configuration || {};
this.plotOptionsForm = new PlotOptionsForm(topic);
/*
* Determine whether the changes to the model that triggered a
* mutation event were purely compositional.
*/
function hasCompositionChanged(oldComposition, newComposition){
// Framed slightly strangely, but the boolean logic is
// easier to follow for the unchanged case.
var isUnchanged = oldComposition === newComposition ||
(
oldComposition.length === newComposition.length &&
oldComposition.every( function (currentValue, index) {
return newComposition[index] && currentValue === newComposition[index];
})
);
return !isUnchanged;
}
]
}]},
yAxisForm = {
'name':'y-axis',
'sections': [{
// Will need to be repeated for each y-axis, with a
// distinct name each. Ideally the name of the axis itself.
'name': 'y-axis',
'rows': [
{
'name': 'Autoscale',
'control': 'checkbox',
'key': 'autoscale',
},
{
'name': 'Min',
'control': 'textfield',
'key': 'min',
'pattern': '[0-9]'
},
{
'name': 'Max',
'control': 'textfield',
'key': 'min',
'pattern': '[0-9]'
},
{
'name': 'Range',
'control': 'select',
'key': 'key',
'options': [
{'name':'eu', 'value': 'eu'},
{'name':'dn', 'value': 'dn'},
{'name':'status', 'value': 'status'}
]
/*
Default the plot options model
*/
function defaultConfiguration() {
configuration.plot = configuration.plot || {};
configuration.plot.xAxis = configuration.plot.xAxis || {};
configuration.plot.yAxis = configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
configuration.plot.series = configuration.plot.series || {}; // series will be associative array keyed on sub-object id
$scope.configuration = configuration;
}
]
}]
},
plotSeriesForm = {
// For correctness of the rendered markup, repeated forms
// will probably need to have unique names.
'name': 'plotSeries',
'sections': [{
'name': 'Plot Series',
'rows': [
{
'name': 'Markers',
'control': 'checkbox',
'key': 'markers'
},
{
'name': 'No Line',
'control': 'radio',
'key': 'noLine'
},
{
'name': 'Step Line',
'control': 'radio',
'key': 'stepLine'
},
{
'name': 'Linear Line',
'control': 'radio',
'key': 'linearLine'
}
]
}]
},
plotOptionsModel = {};
/*domainObject.getModel().configuration.plot.xAxis= {
'key': 'scet'
};
domainObject.getModel().configuration.plot.yAxis = [{
'autoscale': true,
'min': 0,
'max': 15,
'key': 'eu'
}];
domainObject.getModel().configuration.plot.series = [
{
'id': '',
'lineStyle': '',
'color': '#aaddaa',
'interpolation': 'none'
},
//etc
];*/
$scope.plotOptionsStructure = plotSeriesForm;
$scope.plotOptionsModel = plotOptionsModel;
/*
When a child is added to, or removed from a plot, update the
plot options model
*/
function updateChildren() {
domainObject.useCapability('composition').then(function(children){
$scope.children = children;
composition = domainObject.getModel().composition;
children.forEach(function(child){
configuration.plot.series[child.getId()] = configuration.plot.series[child.getId()] || {};
});
});
}
/*
On changes to the form, update the configuration on the domain
object
*/
function updateConfiguration() {
domainObject.useCapability('mutation', function(model){
model.configuration = model.configuration || {};
model.configuration.plot = configuration.plot;
});
}
/*
Set form structures on scope
*/
$scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
$scope.xAxisForm = this.plotOptionsForm.xAxisForm;
$scope.yAxisForm = this.plotOptionsForm.yAxisForm;
/*
Listen for changes to the domain object and update the object's
children.
*/
domainObject.getCapability('mutation').listen(function(model) {
mutationListener = domainObject.getCapability('mutation').listen(function(model) {
if (hasCompositionChanged(composition, model.composition)) {
updateChildren();
}
});
formListener = this.plotOptionsForm.listen(updateConfiguration);
defaultConfiguration();
updateChildren();
$scope.$on("$destroy", function() {
//Clean up any listeners on destruction of controller
mutationListener();
formListener();
});
}
return PlotOptionsController;

View File

@ -0,0 +1,166 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define*/
define(
[],
function () {
"use strict";
/**
* A class for encapsulating structure and behaviour of the plot
* options form
* @memberOf platform/features/layout
* @param topic
* @constructor
*/
function PlotOptionsForm(topic) {
var self = this;
this.onchangeTopic = topic();
function onchange(value){
self.onchangeTopic.notify(value);
}
/*
Defined below are the form structures for the plot options.
*/
this.xAxisForm = {
'name':'x-axis',
'sections': [{
'name': 'x-axis',
'rows': [
{
'name': 'Domain',
'control': 'select',
'key': 'key',
'onchange': onchange,
'options': [
{'name':'scet', 'value': 'scet'},
{'name':'sclk', 'value': 'sclk'},
{'name':'lst', 'value': 'lst'}
]
}
]
}]};
this.yAxisForm = {
'name':'y-axis',
'sections': [{
// Will need to be repeated for each y-axis, with a
// distinct name for each. Ideally the name of the axis
// itself.
'name': 'y-axis',
'rows': [
{
'name': 'Autoscale',
'control': 'checkbox',
'key': 'autoscale',
'onchange': onchange
},
{
'name': 'Min',
'control': 'textfield',
'key': 'min',
'pattern': '[0-9]',
'onchange': onchange
},
{
'name': 'Max',
'control': 'textfield',
'key': 'max',
'pattern': '[0-9]',
'onchange': onchange
},
{
'name': 'Range',
'control': 'select',
'key': 'key',
'onchange': onchange,
'options': [
{'name':'eu', 'value': 'eu'},
{'name':'dn', 'value': 'dn'},
{'name':'status', 'value': 'status'}
]
}
]
}]
};
this.plotSeriesForm = {
'name':'Series Options',
'sections': [
{
rows: [
{
'name': 'Color',
'control': 'color',
'key': 'color',
'onchange': onchange
}]
},
{
'rows':[
{
'name': 'Markers',
'control': 'checkbox',
'key': 'markers',
'onchange': onchange
}
]
},
{
'rows':[
{
'name': 'No Line',
'control': 'radio',
'key': 'lineType',
'value': 'noLine',
'onchange': onchange
},
{
'name': 'Step Line',
'control': 'radio',
'key': 'lineType',
'value': 'stepLine',
'onchange': onchange
},
{
'name': 'Linear Line',
'control': 'radio',
'key': 'lineType',
'value': 'linearLine',
'onchange': onchange
}
]
}
]
};
}
PlotOptionsForm.prototype.listen = function (callback){
return this.onchangeTopic.listen(callback);
};
return PlotOptionsForm;
}
);

View File

@ -0,0 +1,161 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine,xit*/
define(
['../src/PlotOptionsController'],
function (PlotOptionsController) {
"use strict";
describe("The Plot Options controller", function () {
var plotOptionsController,
mockTopicFunction,
mockTopicObject,
mockDomainObject,
mockMutationCapability,
mockUseCapabilities,
mockCompositionCapability,
mockComposition,
mockUnlisten,
mockFormUnlisten,
mockChildOne,
mockChildTwo,
model,
mockScope;
beforeEach(function () {
model = {
composition: ['childOne']
};
mockChildOne = jasmine.createSpyObj('domainObject', [
'getId'
]);
mockChildOne.getId.andReturn('childOne');
mockChildTwo = jasmine.createSpyObj('childTwo', [
'getId'
]);
mockChildOne.getId.andReturn('childTwo');
mockCompositionCapability = jasmine.createSpyObj('compositionCapability', [
'then'
]);
mockComposition = [
mockChildOne
];
mockCompositionCapability.then.andCallFake(function (callback){
callback(mockComposition);
});
mockUseCapabilities = jasmine.createSpyObj('useCapabilities', [
'composition',
'mutation'
]);
mockUseCapabilities.composition.andReturn(mockCompositionCapability);
mockMutationCapability = jasmine.createSpyObj('mutationCapability', [
'listen'
]);
mockUnlisten = jasmine.createSpy('unlisten');
mockMutationCapability.listen.andReturn(mockUnlisten);
mockTopicObject = jasmine.createSpyObj('Topic', [
'listen',
'notify'
]);
mockFormUnlisten = jasmine.createSpy('formUnlisten');
mockTopicObject.listen.andReturn(mockFormUnlisten);
mockTopicFunction = function() {
return mockTopicObject;
};
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getModel',
'useCapability',
'getCapability'
]);
mockDomainObject.useCapability.andCallFake(function(capability){
return mockUseCapabilities[capability]();
});
mockDomainObject.getCapability.andReturn(mockMutationCapability);
mockDomainObject.getModel.andReturn(model);
mockScope = jasmine.createSpyObj('scope', [
'$on'
]);
mockScope.domainObject = mockDomainObject;
plotOptionsController = new PlotOptionsController(mockScope, mockTopicFunction);
});
it("sets form definitions on scope", function () {
expect(mockScope.xAxisForm).toBeDefined();
expect(mockScope.yAxisForm).toBeDefined();
expect(mockScope.plotSeriesForm).toBeDefined();
});
it("sets object children on scope", function () {
expect(mockScope.children).toBe(mockComposition);
});
it("on changes in object composition, updates the form", function () {
expect(mockMutationCapability.listen).toHaveBeenCalled();
expect(mockScope.children).toBe(mockComposition);
expect(mockScope.children.length).toBe(1);
mockComposition.push(mockChildTwo);
model.composition.push('childTwo');
mockMutationCapability.listen.mostRecentCall.args[0](model);
expect(mockScope.children).toBe(mockComposition);
expect(mockScope.children.length).toBe(2);
});
it("on changes in form values, updates the object model", function () {
var scopeConfiguration = mockScope.configuration,
model = mockDomainObject.getModel();
scopeConfiguration.plot.xAxis.key = 'lst';
scopeConfiguration.plot.yAxis.autoScale = true;
scopeConfiguration.plot.yAxis.key = 'eu';
expect(mockTopicObject.listen).toHaveBeenCalled();
mockTopicObject.listen.mostRecentCall.args[0]();
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
mockDomainObject.useCapability.mostRecentCall.args[1](model);
expect(model.configuration.plot.xAxis.key).toBe('lst');
expect(model.configuration.plot.yAxis.autoScale).toBe(true);
expect(model.configuration.plot.yAxis.key).toBe('eu');
});
it("cleans up listeners on destruction of the controller", function () {
mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled();
expect(mockFormUnlisten).toHaveBeenCalled();
});
});
}
);

View File

@ -0,0 +1,73 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine,xit*/
define(
['../src/PlotOptionsForm'],
function (PlotOptionsForm) {
"use strict";
describe("The Plot Options form", function () {
var plotOptionsForm,
mockTopicFunction,
mockTopicObject,
listener;
beforeEach(function () {
mockTopicObject = jasmine.createSpyObj('Topic', [
'listen',
'notify'
]);
mockTopicFunction = function() {
return mockTopicObject;
};
plotOptionsForm = new PlotOptionsForm(mockTopicFunction);
});
it("defines form specs for x-axis, y-axis, and series data", function () {
expect(plotOptionsForm.xAxisForm).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
expect(plotOptionsForm.yAxisForm).toBeDefined();
expect(plotOptionsForm.plotSeriesForm).toBeDefined();
});
it("uses a topic to register listeners and inform them when a" +
" form value changes", function () {
var changedValue = 'changedValue';
expect(plotOptionsForm.xAxisForm.sections[0].rows[0].onchange).toBeDefined();
plotOptionsForm.listen(listener);
expect(mockTopicObject.listen).toHaveBeenCalledWith(listener);
plotOptionsForm.xAxisForm.sections[0].rows[0].onchange(changedValue);
expect(mockTopicObject.notify).toHaveBeenCalledWith(changedValue);
});
});
}
);

View File

@ -23,6 +23,7 @@
<input type="checkbox"
name="mctControl"
ng-model="ngModel[field]"
ng-change="structure.onchange(ngModel[field])"
ng-disabled="ngDisabled">
<em></em>
</label>

View File

@ -23,6 +23,8 @@
<input type="radio"
name="mctControl"
ng-model="ngModel[field]"
ng-disabled="ngDisabled">
ng-disabled="ngDisabled"
ng-change="structure.onchange(ngModel[field])"
ng-value="structure.value">
<em></em>
</label>

View File

@ -24,6 +24,7 @@
ng-model="ngModel[field]"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
ng-change="structure.onchange(ngModel[field])"
name="mctControl">
<option value="" ng-show="!ngModel[field]">- Select One -</option>
</select>

View File

@ -25,6 +25,7 @@
ng-required="ngRequired"
ng-model="ngModel[field]"
ng-pattern="ngPattern"
ng-change="structure.onchange(ngModel[field])"
size="{{structure.size}}"
name="mctControl">
</span>

View File

@ -41,6 +41,7 @@
<mct-control key="row.control"
ng-model="ngModel"
ng-required="row.required"
ng-change="row.onchange"
ng-pattern="getRegExp(row.pattern)"
options="row.options"
structure="row"