mirror of
https://github.com/nasa/openmct.git
synced 2025-02-07 19:40:28 +00:00
Merge remote-tracking branch 'origin/open684' into open-master
This commit is contained in:
commit
cef1a34c64
@ -1,4 +1,7 @@
|
|||||||
<div ng-controller="ExampleFormController">
|
<div ng-controller="ExampleFormController">
|
||||||
|
<mct-toolbar structure="toolbar" ng-model="state" name="aToolbar">
|
||||||
|
</mct-toolbar>
|
||||||
|
|
||||||
<mct-form structure="form" ng-model="state" name="aForm">
|
<mct-form structure="form" ng-model="state" name="aForm">
|
||||||
</mct-form>
|
</mct-form>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*global define*/
|
/*global define,window*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
[],
|
[],
|
||||||
@ -10,6 +10,89 @@ define(
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.toolbar = {
|
||||||
|
name: "An example toolbar.",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
description: "First section",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: "X",
|
||||||
|
description: "X coordinate",
|
||||||
|
control: "textfield",
|
||||||
|
pattern: "^\\d+$",
|
||||||
|
disabled: true,
|
||||||
|
size: 2,
|
||||||
|
key: "x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Y",
|
||||||
|
description: "Y coordinate",
|
||||||
|
control: "textfield",
|
||||||
|
pattern: "^\\d+$",
|
||||||
|
size: 2,
|
||||||
|
key: "y"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "W",
|
||||||
|
description: "Cell width",
|
||||||
|
control: "textfield",
|
||||||
|
pattern: "^\\d+$",
|
||||||
|
size: 2,
|
||||||
|
key: "w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "H",
|
||||||
|
description: "Cell height",
|
||||||
|
control: "textfield",
|
||||||
|
pattern: "^\\d+$",
|
||||||
|
size: 2,
|
||||||
|
key: "h"
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Second section",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
control: "button",
|
||||||
|
glyph: "1",
|
||||||
|
description: "Button A",
|
||||||
|
click: function () {
|
||||||
|
window.alert("A");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
control: "button",
|
||||||
|
glyph: "2",
|
||||||
|
description: "Button B",
|
||||||
|
click: function () {
|
||||||
|
window.alert("B");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
control: "button",
|
||||||
|
glyph: "3",
|
||||||
|
description: "Button C",
|
||||||
|
disabled: true,
|
||||||
|
click: function () {
|
||||||
|
window.alert("C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
control: "color",
|
||||||
|
key: "color"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
$scope.form = {
|
$scope.form = {
|
||||||
name: "An example form.",
|
name: "An example form.",
|
||||||
sections: [
|
sections: [
|
||||||
|
@ -12,6 +12,11 @@ directive, e.g.:
|
|||||||
<mct-form ng-model="myModel" structure="myStructure" name="myForm">
|
<mct-form ng-model="myModel" structure="myStructure" name="myForm">
|
||||||
</mct-form>
|
</mct-form>
|
||||||
|
|
||||||
|
Using toolbars is similar:
|
||||||
|
|
||||||
|
<mct-toolbar ng-model="myModel" structure="myStructure" name="myToolbar">
|
||||||
|
</mct-toolbar>
|
||||||
|
|
||||||
The attributes utilized by this form are as follows:
|
The attributes utilized by this form are as follows:
|
||||||
|
|
||||||
* `ng-model`: The object which should contain the full form input. Individual
|
* `ng-model`: The object which should contain the full form input. Individual
|
||||||
@ -55,6 +60,44 @@ Note that `pattern` may be specified as a string, to simplify storing
|
|||||||
for structures as JSON when necessary. The string should be given in
|
for structures as JSON when necessary. The string should be given in
|
||||||
a form appropriate to pass to a `RegExp` constructor.
|
a form appropriate to pass to a `RegExp` constructor.
|
||||||
|
|
||||||
|
## Toolbar structure
|
||||||
|
|
||||||
|
A toolbar's structure is described similarly to forms, except that there
|
||||||
|
is no notion of rows; instead, there are `items`.
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": ... title to display for the form, as a string ...,
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"name": ... title to display for the section ...,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": ... title to display for this row ...,
|
||||||
|
"control": ... symbolic key for the control ...,
|
||||||
|
"key": ... field name in ng-model ...
|
||||||
|
"pattern": ... optional, reg exp to match against ...
|
||||||
|
"required": ... optional boolean ...
|
||||||
|
"options": [
|
||||||
|
"name": ... name to display (e.g. in a select) ...,
|
||||||
|
"value": ... value to store in the model ...
|
||||||
|
],
|
||||||
|
"disabled": ... true if control should be disabled ...
|
||||||
|
"size": ... size of the control (for textfields) ...
|
||||||
|
"click": ... function to invoke (for buttons) ...
|
||||||
|
"glyph": ... glyph to display (for buttons) ...
|
||||||
|
"text": ... text withiin control (for buttons) ...
|
||||||
|
},
|
||||||
|
... and other rows ...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
... and other sections ...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that `pattern` may be specified as a string, to simplify storing
|
||||||
|
for structures as JSON when necessary. The string should be given in
|
||||||
|
a form appropriate to pass to a `RegExp` constructor.
|
||||||
|
|
||||||
## Adding controls
|
## Adding controls
|
||||||
|
|
||||||
Four standard control types are included in the forms bundle:
|
Four standard control types are included in the forms bundle:
|
||||||
@ -62,6 +105,8 @@ Four standard control types are included in the forms bundle:
|
|||||||
* `textfield`: An area to enter plain text.
|
* `textfield`: An area to enter plain text.
|
||||||
* `select`: A drop-down list of options.
|
* `select`: A drop-down list of options.
|
||||||
* `checkbox`: A box which may be checked/unchecked.
|
* `checkbox`: A box which may be checked/unchecked.
|
||||||
|
* `color`: A color picker.
|
||||||
|
* `button`: A button.
|
||||||
* `datetime`: An input for UTC date/time entry; gives result as a
|
* `datetime`: An input for UTC date/time entry; gives result as a
|
||||||
UNIX timestamp, in milliseconds since start of 1970, UTC.
|
UNIX timestamp, in milliseconds since start of 1970, UTC.
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
"key": "mctForm",
|
"key": "mctForm",
|
||||||
"implementation": "MCTForm.js"
|
"implementation": "MCTForm.js"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "mctToolbar",
|
||||||
|
"implementation": "MCTToolbar.js"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "mctControl",
|
"key": "mctControl",
|
||||||
"implementation": "MCTControl.js",
|
"implementation": "MCTControl.js",
|
||||||
@ -30,6 +34,14 @@
|
|||||||
"key": "textfield",
|
"key": "textfield",
|
||||||
"templateUrl": "templates/controls/textfield.html"
|
"templateUrl": "templates/controls/textfield.html"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "button",
|
||||||
|
"templateUrl": "templates/controls/button.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "color",
|
||||||
|
"templateUrl": "templates/controls/color.html"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "composite",
|
"key": "composite",
|
||||||
"templateUrl": "templates/controls/composite.html"
|
"templateUrl": "templates/controls/composite.html"
|
||||||
@ -45,36 +57,6 @@
|
|||||||
"key": "CompositeController",
|
"key": "CompositeController",
|
||||||
"implementation": "controllers/CompositeController.js"
|
"implementation": "controllers/CompositeController.js"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"templates": [
|
|
||||||
{
|
|
||||||
"key": "_checkbox",
|
|
||||||
"templateUrl": "templates/_checkbox.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_checkboxes",
|
|
||||||
"templateUrl": "templates/_checkboxes.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_datetime",
|
|
||||||
"templateUrl": "templates/_datetime.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_select",
|
|
||||||
"templateUrl": "templates/_select.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_selects",
|
|
||||||
"templateUrl": "templates/_selects.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_textfield",
|
|
||||||
"templateUrl": "templates/_textfield.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "_textfields",
|
|
||||||
"templateUrl": "templates/_textfields.html"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
11
platform/forms/res/templates/controls/button.html
Normal file
11
platform/forms/res/templates/controls/button.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<a href=""
|
||||||
|
class="t-btn l-btn s-btn s-icon-btn s-very-subtle"
|
||||||
|
ng-class="{ labeled: structure.text }"
|
||||||
|
ng-click="structure.click()">
|
||||||
|
<span class="ui-symbol icon">
|
||||||
|
{{structure.glyph}}
|
||||||
|
</span>
|
||||||
|
<span class="title-label" ng-if="structure.text">
|
||||||
|
{{structure.text}}
|
||||||
|
</span>
|
||||||
|
</a>
|
3
platform/forms/res/templates/controls/color.html
Normal file
3
platform/forms/res/templates/controls/color.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<input type="color"
|
||||||
|
name="mctControl"
|
||||||
|
ng-model="ngModel[field]">
|
@ -4,6 +4,7 @@
|
|||||||
ng-required="ngRequired"
|
ng-required="ngRequired"
|
||||||
ng-model="ngModel[field]"
|
ng-model="ngModel[field]"
|
||||||
ng-pattern="ngPattern"
|
ng-pattern="ngPattern"
|
||||||
|
size="{{structure.size}}"
|
||||||
name="mctControl">
|
name="mctControl">
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
32
platform/forms/res/templates/toolbar.html
Normal file
32
platform/forms/res/templates/toolbar.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<form name="tool-bar btn-bar contents" novalidate>
|
||||||
|
|
||||||
|
<div class="form">
|
||||||
|
<span ng-repeat="section in structure.sections"
|
||||||
|
class="control-group coordinates"
|
||||||
|
title="{{section.description}}">
|
||||||
|
|
||||||
|
<ng-form ng-repeat="item in section.items"
|
||||||
|
ng-class="{ 'input-labeled': item.name }"
|
||||||
|
class="inline"
|
||||||
|
title="{{item.description}}"
|
||||||
|
name="mctFormInner">
|
||||||
|
|
||||||
|
<label ng-if="item.name">
|
||||||
|
{{item.name}}:
|
||||||
|
</label>
|
||||||
|
<mct-control key="item.control"
|
||||||
|
ng-class="{ disabled: item.disabled }"
|
||||||
|
ng-model="ngModel"
|
||||||
|
ng-required="item.required"
|
||||||
|
ng-pattern="getRegExp(item.pattern)"
|
||||||
|
options="item.options"
|
||||||
|
structure="item"
|
||||||
|
field="item.key">
|
||||||
|
</mct-control>
|
||||||
|
|
||||||
|
</ng-form>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
@ -4,13 +4,10 @@
|
|||||||
* Module defining MCTForm. Created by vwoeltje on 11/10/14.
|
* Module defining MCTForm. Created by vwoeltje on 11/10/14.
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
[],
|
["./controllers/FormController"],
|
||||||
function () {
|
function (FormController) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Default ng-pattern; any non whitespace
|
|
||||||
var NON_WHITESPACE = /\S/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mct-form directive allows generation of displayable
|
* The mct-form directive allows generation of displayable
|
||||||
* forms based on a declarative description of the form's
|
* forms based on a declarative description of the form's
|
||||||
@ -37,45 +34,6 @@ define(
|
|||||||
"templates/form.html"
|
"templates/form.html"
|
||||||
].join("/");
|
].join("/");
|
||||||
|
|
||||||
function controller($scope) {
|
|
||||||
var regexps = [];
|
|
||||||
|
|
||||||
// ng-pattern seems to want a RegExp, and not a
|
|
||||||
// string (despite what documentation says) but
|
|
||||||
// we want form structure to be JSON-expressible,
|
|
||||||
// so we make RegExp's from strings as-needed
|
|
||||||
function getRegExp(pattern) {
|
|
||||||
// If undefined, don't apply a pattern
|
|
||||||
if (!pattern) {
|
|
||||||
return NON_WHITESPACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just echo if it's already a regexp
|
|
||||||
if (pattern instanceof RegExp) {
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, assume a string
|
|
||||||
// Cache for easy lookup later (so we don't
|
|
||||||
// creat a new RegExp every digest cycle)
|
|
||||||
if (!regexps[pattern]) {
|
|
||||||
regexps[pattern] = new RegExp(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
return regexps[pattern];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish the form state under the requested
|
|
||||||
// name in the parent scope
|
|
||||||
$scope.$watch("mctForm", function (mctForm) {
|
|
||||||
if ($scope.name) {
|
|
||||||
$scope.$parent[$scope.name] = mctForm;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.getRegExp = getRegExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Only show at the element level
|
// Only show at the element level
|
||||||
restrict: "E",
|
restrict: "E",
|
||||||
@ -83,9 +41,8 @@ define(
|
|||||||
// Load the forms template
|
// Load the forms template
|
||||||
templateUrl: templatePath,
|
templateUrl: templatePath,
|
||||||
|
|
||||||
// Use the controller defined above to
|
// Use FormController to populate/respond to changes in scope
|
||||||
// populate/respond to changes in scope
|
controller: FormController,
|
||||||
controller: controller,
|
|
||||||
|
|
||||||
// Initial an isolate scope
|
// Initial an isolate scope
|
||||||
scope: {
|
scope: {
|
||||||
|
64
platform/forms/src/MCTToolbar.js
Normal file
64
platform/forms/src/MCTToolbar.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*global define,Promise*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining MCTForm. Created by vwoeltje on 11/10/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["./controllers/FormController"],
|
||||||
|
function (FormController) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mct-toolbar directive allows generation of displayable
|
||||||
|
* forms based on a declarative description of the form's
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* This directive accepts three attributes:
|
||||||
|
*
|
||||||
|
* * `ng-model`: The model for the form; where user input
|
||||||
|
* where be stored.
|
||||||
|
* * `structure`: The declarative structure of the toolbar.
|
||||||
|
* Describes what controls should be shown and where
|
||||||
|
* their values should be read/written in the model.
|
||||||
|
* * `name`: The name under which to expose the form's
|
||||||
|
* dirty/valid state. This is similar to ng-form's use
|
||||||
|
* of name, except this will be made available in the
|
||||||
|
* parent scope.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function MCTForm() {
|
||||||
|
var templatePath = [
|
||||||
|
"platform/forms", //MCTForm.bundle.path,
|
||||||
|
"res", //MCTForm.bundle.resources,
|
||||||
|
"templates/toolbar.html"
|
||||||
|
].join("/");
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Only show at the element level
|
||||||
|
restrict: "E",
|
||||||
|
|
||||||
|
// Load the forms template
|
||||||
|
templateUrl: templatePath,
|
||||||
|
|
||||||
|
// Use FormController to populate/respond to changes in scope
|
||||||
|
controller: FormController,
|
||||||
|
|
||||||
|
// Initial an isolate scope
|
||||||
|
scope: {
|
||||||
|
|
||||||
|
// The model: Where form input will actually go
|
||||||
|
ngModel: "=",
|
||||||
|
|
||||||
|
// Form structure; what sections/rows to show
|
||||||
|
structure: "=",
|
||||||
|
|
||||||
|
// Name under which to publish the form
|
||||||
|
name: "@"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTForm;
|
||||||
|
}
|
||||||
|
);
|
57
platform/forms/src/controllers/FormController.js
Normal file
57
platform/forms/src/controllers/FormController.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Default ng-pattern; any non whitespace
|
||||||
|
var NON_WHITESPACE = /\S/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for mct-form and mct-toolbar directives.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function FormController($scope) {
|
||||||
|
var regexps = [];
|
||||||
|
|
||||||
|
// ng-pattern seems to want a RegExp, and not a
|
||||||
|
// string (despite what documentation says) but
|
||||||
|
// we want form structure to be JSON-expressible,
|
||||||
|
// so we make RegExp's from strings as-needed
|
||||||
|
function getRegExp(pattern) {
|
||||||
|
// If undefined, don't apply a pattern
|
||||||
|
if (!pattern) {
|
||||||
|
return NON_WHITESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just echo if it's already a regexp
|
||||||
|
if (pattern instanceof RegExp) {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, assume a string
|
||||||
|
// Cache for easy lookup later (so we don't
|
||||||
|
// creat a new RegExp every digest cycle)
|
||||||
|
if (!regexps[pattern]) {
|
||||||
|
regexps[pattern] = new RegExp(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
return regexps[pattern];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish the form state under the requested
|
||||||
|
// name in the parent scope
|
||||||
|
$scope.$watch("mctForm", function (mctForm) {
|
||||||
|
if ($scope.name) {
|
||||||
|
$scope.$parent[$scope.name] = mctForm;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose the regexp lookup
|
||||||
|
$scope.getRegExp = getRegExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormController;
|
||||||
|
}
|
||||||
|
);
|
90
platform/forms/test/MCTToolbarSpec.js
Normal file
90
platform/forms/test/MCTToolbarSpec.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../src/MCTToolbar"],
|
||||||
|
function (MCTToolbar) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The mct-toolbar directive", function () {
|
||||||
|
var mockScope,
|
||||||
|
mctToolbar;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope = jasmine.createSpyObj("$scope", [ "$watch" ]);
|
||||||
|
mockScope.$parent = {};
|
||||||
|
mctToolbar = new MCTToolbar();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is restricted to elements", function () {
|
||||||
|
expect(mctToolbar.restrict).toEqual("E");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("watches for changes in form by name", function () {
|
||||||
|
// mct-form needs to watch for the form by name
|
||||||
|
// in order to convey changes in $valid, $dirty, etc
|
||||||
|
// up to the parent scope.
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
|
"mctForm",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("conveys form status to parent scope", function () {
|
||||||
|
var someState = { someKey: "some value" };
|
||||||
|
mockScope.name = "someName";
|
||||||
|
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
|
||||||
|
mockScope.$watch.mostRecentCall.args[1](someState);
|
||||||
|
|
||||||
|
expect(mockScope.$parent.someName).toBe(someState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows strings to be converted to RegExps", function () {
|
||||||
|
// This is needed to support ng-pattern in the template
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
|
||||||
|
// Should have added getRegExp to the scope,
|
||||||
|
// to convert strings to regular expressions
|
||||||
|
expect(mockScope.getRegExp("^\\d+$")).toEqual(/^\d+$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the same regexp instance for the same string", function () {
|
||||||
|
// Don't want new instances each digest cycle, for performance
|
||||||
|
var strRegExp = "^[a-z]\\d+$",
|
||||||
|
regExp;
|
||||||
|
|
||||||
|
// Add getRegExp to scope
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
regExp = mockScope.getRegExp(strRegExp);
|
||||||
|
|
||||||
|
// Same object instance each time...
|
||||||
|
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||||
|
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes RegExp objects through untouched", function () {
|
||||||
|
// Permit using forms to simply provide their own RegExp object
|
||||||
|
var regExp = /^\d+[a-d]$/;
|
||||||
|
|
||||||
|
// Add getRegExp to scope
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
|
||||||
|
// Should have added getRegExp to the scope,
|
||||||
|
// to convert strings to regular expressions
|
||||||
|
expect(mockScope.getRegExp(regExp)).toBe(regExp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes a non-whitespace regexp when no pattern is defined", function () {
|
||||||
|
// If no pattern is supplied, ng-pattern should match anything
|
||||||
|
mctToolbar.controller(mockScope);
|
||||||
|
expect(mockScope.getRegExp()).toEqual(/\S/);
|
||||||
|
expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
69
platform/forms/test/controllers/FormControllerSpec.js
Normal file
69
platform/forms/test/controllers/FormControllerSpec.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/controllers/FormController"],
|
||||||
|
function (FormController) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The form controller", function () {
|
||||||
|
var mockScope,
|
||||||
|
controller;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope = jasmine.createSpyObj("$scope", [ "$watch" ]);
|
||||||
|
mockScope.$parent = {};
|
||||||
|
controller = new FormController(mockScope);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("watches for changes in form by name", function () {
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
|
"mctForm",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("conveys form status to parent scope", function () {
|
||||||
|
var someState = { someKey: "some value" };
|
||||||
|
mockScope.name = "someName";
|
||||||
|
mockScope.$watch.mostRecentCall.args[1](someState);
|
||||||
|
expect(mockScope.$parent.someName).toBe(someState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows strings to be converted to RegExps", function () {
|
||||||
|
// Should have added getRegExp to the scope,
|
||||||
|
// to convert strings to regular expressions
|
||||||
|
expect(mockScope.getRegExp("^\\d+$")).toEqual(/^\d+$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the same regexp instance for the same string", function () {
|
||||||
|
// Don't want new instances each digest cycle, for performance
|
||||||
|
var strRegExp = "^[a-z]\\d+$",
|
||||||
|
regExp;
|
||||||
|
|
||||||
|
// Add getRegExp to scope
|
||||||
|
regExp = mockScope.getRegExp(strRegExp);
|
||||||
|
|
||||||
|
// Same object instance each time...
|
||||||
|
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||||
|
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes RegExp objects through untouched", function () {
|
||||||
|
// Permit using forms to simply provide their own RegExp object
|
||||||
|
var regExp = /^\d+[a-d]$/;
|
||||||
|
|
||||||
|
// Should have added getRegExp to the scope,
|
||||||
|
// to convert strings to regular expressions
|
||||||
|
expect(mockScope.getRegExp(regExp)).toBe(regExp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes a non-whitespace regexp when no pattern is defined", function () {
|
||||||
|
// If no pattern is supplied, ng-pattern should match anything
|
||||||
|
expect(mockScope.getRegExp()).toEqual(/\S/);
|
||||||
|
expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -2,5 +2,6 @@
|
|||||||
"MCTControl",
|
"MCTControl",
|
||||||
"MCTForm",
|
"MCTForm",
|
||||||
"controllers/CompositeController",
|
"controllers/CompositeController",
|
||||||
"controllers/DateTimeController"
|
"controllers/DateTimeController",
|
||||||
|
"controllers/FormController"
|
||||||
]
|
]
|
Loading…
x
Reference in New Issue
Block a user