launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06955
[Merge] lp:~huwshimi/maas/morphing-integration into lp:maas
Huw Wilkins has proposed merging lp:~huwshimi/maas/morphing-integration into lp:maas.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~huwshimi/maas/morphing-integration/+merge/100361
This branch converts the add node overlay to be a generic widget and use morphing to show/hide the widget in place.
--
https://code.launchpad.net/~huwshimi/maas/morphing-integration/+merge/100361
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~huwshimi/maas/morphing-integration into lp:maas.
=== added file 'src/maasserver/static/css/components/yui_node_add.css'
--- src/maasserver/static/css/components/yui_node_add.css 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/css/components/yui_node_add.css 2012-04-02 06:48:18 +0000
@@ -0,0 +1,6 @@
+.yui3-node-add-widget {
+ width: 360px;
+ }
+.yui3-node-add-widget .buttons {
+ margin-top: 30px;
+ }
=== modified file 'src/maasserver/static/css/forms.css'
--- src/maasserver/static/css/forms.css 2012-03-26 05:17:47 +0000
+++ src/maasserver/static/css/forms.css 2012-04-02 06:48:18 +0000
@@ -127,6 +127,10 @@
background-image: -webkit-linear-gradient(bottom, rgb(51,51,51) 0%, rgb(90,90,90) 100%);
background-image: -ms-linear-gradient(bottom, rgb(51,51,51) 0%, rgb(90,90,90) 100%);
}
+.link-button {
+ display: inline-block;
+ padding: 6px 0;
+ }
.spinner {
float: right;
margin: 8px 10px 0 0;
=== modified file 'src/maasserver/static/css/import.css'
--- src/maasserver/static/css/import.css 2012-03-01 06:22:38 +0000
+++ src/maasserver/static/css/import.css 2012-04-02 06:48:18 +0000
@@ -11,5 +11,6 @@
@import url("components/blocks.css");
@import url("components/yui_panel.css");
@import url("components/yui_overlay.css");
+@import url("components/yui_node_add.css");
@import url("components/data_list.css");
@import url("components/search_box.css");
=== modified file 'src/maasserver/static/js/morph.js'
--- src/maasserver/static/js/morph.js 2012-03-19 01:14:42 +0000
+++ src/maasserver/static/js/morph.js 2012-04-02 06:48:18 +0000
@@ -43,15 +43,14 @@
var srcNode = this.get('srcNode');
var targetNode = this.get('targetNode');
}
-
- target_height = targetNode.getComputedStyle('height');
+ var target_height = targetNode.getComputedStyle('height');
var fade_out = new Y.Anim({
node: targetNode,
to: {opacity: 0},
duration: 0.2,
easing: 'easeOut'
});
- fade_out.run();
+ var self = this;
fade_out.on('end', function () {
targetNode.addClass('hidden');
srcNode.setStyle('opacity', 0);
@@ -70,9 +69,14 @@
duration: 0.5,
easing: 'easeOut'
});
+ resize.on('end', function () {
+ srcNode.setStyle('height', 'auto');
+ self.fire('morphed');
+ });
fade_in.run();
resize.run();
});
+ fade_out.run();
}
});
=== modified file 'src/maasserver/static/js/node_add.js'
--- src/maasserver/static/js/node_add.js 2012-03-26 05:27:45 +0000
+++ src/maasserver/static/js/node_add.js 2012-04-02 06:48:18 +0000
@@ -36,10 +36,30 @@
return this.get(
'srcNode').all('input[name=mac_addresses]').size();
}
+ },
+
+ /**
+ * The DOM node to be morphed from.
+ *
+ * @attribute targetNode
+ * @type string
+ */
+ targetNode: {
+ value: null
+ },
+
+ /**
+ * Set the panel to fade in/out or just appear/disappear
+ *
+ * @attribute animate
+ * @type boolean
+ */
+ animate: {
+ value: true
}
};
-Y.extend(AddNodeWidget, Y.Panel, {
+Y.extend(AddNodeWidget, Y.Widget, {
/**
* Create an input field to add a MAC Address.
@@ -55,23 +75,6 @@
return Y.Node.create('<p />').append(field);
},
- /**
- * Hide the panel.
- *
- * @method hidePanel
- */
- hidePanel: function() {
- var self = this;
- this.get('boundingBox').transition({
- duration: 0.5,
- top: '-400px'
- },
- function () {
- self.hide();
- self.destroy();
- });
- },
-
addMacField: function() {
if (this.get('nb_mac_fields') === 1) {
var label = this.get(
@@ -112,6 +115,15 @@
},
createForm: function() {
+ var addnode_button = Y.Node.create('<button />')
+ .addClass('add-node-button')
+ .addClass('right')
+ .set('text', "Add node");
+ var cancel_button = Y.Node.create('<a />')
+ .addClass('cancel-button')
+ .set('href', '#')
+ .set('text', "Cancel")
+ .addClass('link-button');
var macaddress_add_link = Y.Node.create('<a />')
.addClass('add-link')
.addClass('add-mac-form')
@@ -123,6 +135,10 @@
.set('value', 'new');
var global_error = Y.Node.create('<p />')
.addClass('form-errors');
+ var buttons = Y.Node.create('<div />')
+ .addClass('buttons')
+ .append(addnode_button)
+ .append(cancel_button);
var addnodeform = Y.Node.create('<form />')
.set('method', 'post')
.append(global_error)
@@ -130,7 +146,8 @@
.append(Y.Node.create(this.add_macaddress))
.append(macaddress_add_link)
.append(Y.Node.create(this.add_architecture))
- .append(Y.Node.create(this.add_node));
+ .append(Y.Node.create(this.add_node))
+ .append(buttons);
return addnodeform;
},
@@ -162,8 +179,8 @@
* @method showSpinner
*/
showSpinner: function() {
- var buttons = this.get('srcNode').one('.yui3-widget-button-wrapper');
- buttons.append(this.spinnerNode);
+ var buttons = this.get('srcNode').one('.add-node-button');
+ buttons.insert(this.spinnerNode, 'after');
},
/**
@@ -176,16 +193,60 @@
},
initializer: function(cfg) {
+ this.get('srcNode').addClass('hidden');
+ this.morpher = new Y.maas.morph.Morph(
+ {srcNode: cfg.srcNode, targetNode: this.get('targetNode')});
+ },
+
+ renderUI: function() {
// Load form snippets.
this.add_macaddress = Y.one('#add-macaddress').getContent();
this.add_architecture = Y.one('#add-architecture').getContent();
this.add_node = Y.one('#add-node').getContent();
// Create panel's content.
- this.set('bodyContent', this.createForm());
+ var heading = Y.Node.create('<h2 />')
+ .set('text', "Add node");
+ this.get('srcNode').append(heading).append(this.createForm());
this.initializeNodes();
},
/**
+ * Show the widget
+ *
+ * @method showWidget
+ */
+ showWidget: function() {
+ if (this.get('animate')) {
+ this.morpher.morph();
+ this.morpher.on('morphed', function(e, widget) {
+ widget.get('srcNode').one('input[type=text]').focus();
+ }, null, this);
+ }
+ else {
+ Y.one(this.get('targetNode')).addClass('hidden');
+ this.get('srcNode').removeClass('hidden');
+ }
+ },
+
+ /**
+ * Hide the widget
+ *
+ * @method showWidget
+ */
+ hideWidget: function() {
+ if (this.get('animate')) {
+ this.morpher.morph('reverse');
+ this.morpher.on('morphed', function(e, widget) {
+ widget.destroy();
+ }, null, this);
+ }
+ else {
+ this.get('srcNode').addClass('hidden');
+ Y.one(this.get('targetNode')).removeClass('hidden');
+ }
+ },
+
+ /**
* Initialize the nodes this widget will use.
*
* @method initializeNodes
@@ -207,14 +268,22 @@
bindUI: function() {
var self = this;
- this.get(
- 'bodyContent').one('.add-mac-form').on('click', function(e) {
+ var srcNode = this.get('srcNode');
+ srcNode.one('.add-mac-form').on('click', function(e) {
e.preventDefault();
self.addMacField();
});
- this.get('bodyContent').on('key', function() {
+ srcNode.on('key', function() {
self.sendAddNodeRequest();
}, 'press:enter');
+ srcNode.one('.add-node-button').on('click', function(e) {
+ e.preventDefault();
+ self.sendAddNodeRequest();
+ });
+ srcNode.one('.cancel-button').on('click', function(e, widget) {
+ e.preventDefault();
+ widget.hideWidget();
+ }, null, this);
},
addNode: function(node) {
@@ -231,7 +300,7 @@
start: Y.bind(self.showSpinner, self),
success: function(id, out) {
self.addNode(JSON.parse(out.response));
- self.hidePanel();
+ self.hideWidget();
},
failure: function(id, out) {
Y.log("Adding a node failed. Response object follows.")
@@ -297,11 +366,7 @@
*
* @method showAddNodeWidget
*/
-module.showAddNodeWidget = function(event) {
- // Cope with manual calls as well as event calls.
- if (Y.Lang.isValue(event)) {
- event.preventDefault();
- }
+module.showAddNodeWidget = function(cfg) {
// If a widget is already present, destroy it.
var destroy = (
Y.Lang.isValue(module._add_node_singleton) &&
@@ -309,48 +374,17 @@
if (destroy) {
module._add_node_singleton.destroy();
}
- var cfg = {
- headerContent: "Add node",
- buttons: [
- {
- value: 'Add node',
- section: 'footer',
- action: function (e) {
- e.preventDefault();
- this.sendAddNodeRequest();
- }
- },
- {
- value: 'Cancel',
- section: 'footer',
- classNames: 'link-button',
- action: function (e) {
- e.preventDefault();
- this.hidePanel();
- }
- }],
- align: {
- node:'',
- points:
- [Y.WidgetPositionAlign.BC, Y.WidgetPositionAlign.TC]
- },
- modal: true,
- zIndex: 2,
- visible: true,
- render: true,
- hideOn: []
- };
+
+ var add_node_id = 'add-node-widget';
+ cfg.srcNode = '#' + add_node_id
+ var srcNode = Y.Node.create('<div />')
+ .set('id', add_node_id);
+ Y.one(cfg.targetNode).insert(srcNode, 'after');
module._add_node_singleton = new AddNodeWidget(cfg);
- module._add_node_singleton.get('boundingBox').transition({
- duration: 0.5,
- top: '0px'
- });
- // We need to set the focus late as the widget wants to set the focus
- // on the bounding box.
- module._add_node_singleton.get(
- 'boundingBox').one('input[type=text]').focus();
+ module._add_node_singleton.render();
+ module._add_node_singleton.showWidget();
};
-}, '0.1', {'requires': ['io', 'node', 'panel', 'event', 'event-custom',
- 'transition']}
+}, '0.1', {'requires': ['io', 'node', 'widget', 'event', 'event-custom',
+ 'maas.morph']}
);
=== modified file 'src/maasserver/static/js/tests/test_morph.js'
--- src/maasserver/static/js/tests/test_morph.js 2012-03-19 01:14:42 +0000
+++ src/maasserver/static/js/tests/test_morph.js 2012-04-02 06:48:18 +0000
@@ -25,6 +25,10 @@
Y.Assert.isTrue(
Y.one('#panel-two').hasClass('hidden'),
'The source panel should initially be hidden');
+ var morphed_fired = false;
+ morpher.on('morphed', function() {
+ morphed_fired = true;
+ });
morpher.morph();
this.wait(function() {
Y.Assert.isTrue(
@@ -33,6 +37,13 @@
Y.Assert.isFalse(
Y.one(cfg.srcNode).hasClass('hidden'),
'The source panel should now be visible');
+ Y.Assert.isTrue(
+ morphed_fired,
+ 'The morphed event should have fired');
+ Y.Assert.areEqual(
+ 'auto',
+ Y.one(cfg.srcNode).getStyle('height'),
+ 'The morpher should set the height back to auto');
/* Fire this morph again, this time for the reverse. */
morpher.morph(true);
this.wait(function() {
=== modified file 'src/maasserver/static/js/tests/test_node_add.html'
--- src/maasserver/static/js/tests/test_node_add.html 2012-03-15 13:58:32 +0000
+++ src/maasserver/static/js/tests/test_node_add.html 2012-04-02 06:48:18 +0000
@@ -8,6 +8,7 @@
<script type="text/javascript" src="../../jslibs/yui/tests/build/yui/yui.js"></script>
<script type="text/javascript" src="../testing/testrunner.js"></script>
<script type="text/javascript" src="../testing/testing.js"></script>
+ <script type="text/javascript" src="../morph.js"></script>
<!-- The module under test -->
<script type="text/javascript" src="../node_add.js"></script>
<!-- The test suite -->
@@ -47,7 +48,7 @@
<input type="text" maxlength="30" name="hostname" id="id_hostname" />
</p>
</script>
-
+ <div id="target_node"></div>
<span id="suite">maas.node_add.tests</span>
</body>
</html>
=== modified file 'src/maasserver/static/js/tests/test_node_add.js'
--- src/maasserver/static/js/tests/test_node_add.js 2012-03-27 06:50:37 +0000
+++ src/maasserver/static/js/tests/test_node_add.js 2012-04-02 06:48:18 +0000
@@ -26,14 +26,14 @@
testSingletonCreation: function() {
// module._add_node_singleton is originally null.
Y.Assert.isNull(module._add_node_singleton);
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
// module._add_node_singleton is populated after the call to
// module.showAddNodeWidget.
Y.Assert.isNotNull(module._add_node_singleton);
},
testSingletonReCreation: function() {
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
var panel = module._add_node_singleton;
// Make sure that a second call to showAddNodeWidget destroys
@@ -42,7 +42,7 @@
panel.on("destroy", function(){
destroyed = true;
});
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
Y.Assert.isTrue(destroyed);
Y.Assert.isNotNull(module._add_node_singleton);
Y.Assert.areNotSame(panel, namespace._add_node_singleton);
@@ -75,7 +75,7 @@
/* Find the "Add node" button at the bottom of the add-node form.
*/
function find_add_button() {
- return find_widget().one('.yui3-button');
+ return find_widget().one('.add-node-button');
}
@@ -89,7 +89,7 @@
/* Set up and submit the add-node form.
*/
function submit_add_node() {
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
find_hostname_input().set('value', 'host');
find_add_button().simulate('click');
}
@@ -100,7 +100,7 @@
testFormContainsArchitectureChoice: function() {
// The generated form contains an 'architecture' field.
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
var arch = find_form().one('#id_architecture');
Y.Assert.isNotNull(arch);
var arch_options = arch.all('option');
@@ -110,7 +110,7 @@
testAddNodeAPICallSubmitsForm: function() {
// The call to the API triggered by clicking on 'Add a node'
// submits (via an API call) the panel's form.
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
var mockXhr = new Y.Base();
var form = find_form();
var log = this.logIO(module);
@@ -126,7 +126,7 @@
args: [MAAS_config.uris.nodes_handler, Y.Mock.Value.Any]
});
this.mockIO(mockXhr, module);
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
find_hostname_input().set('value', 'host');
find_add_button().simulate('click');
Y.Mock.verify(mockXhr);
@@ -139,7 +139,7 @@
args: [MAAS_config.uris.nodes_handler, Y.Mock.Value.Any]
});
this.mockIO(mockXhr, module);
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
find_hostname_input().set('value', 'host');
// Simulate 'Enter' being pressed.
find_form().simulate("keypress", { keyCode: 13 });
@@ -148,7 +148,7 @@
testNodeidPopulation: function() {
this.mockSuccess(Y.JSON.stringify({system_id: 3}), module);
- module.showAddNodeWidget();
+ module.showAddNodeWidget({targetNode: '#target_node', animate: false});
this.addCleanup(
Y.bind(
module._add_node_singleton.destroy,
=== modified file 'src/maasserver/templates/maasserver/index.html'
--- src/maasserver/templates/maasserver/index.html 2012-03-20 05:59:00 +0000
+++ src/maasserver/templates/maasserver/index.html 2012-04-02 06:48:18 +0000
@@ -14,7 +14,7 @@
<!--
YUI().use(
'maas.node_add', 'maas.node','maas.node_views', 'maas.utils',
- 'maas.longpoll',
+ 'maas.longpoll',
function (Y) {
Y.on('load', function() {
// Create Dashboard view.
@@ -26,7 +26,11 @@
reservedNode: '#reserved-nodes',
retiredNode: '#retired-nodes'});
view.render();
- Y.one('#addnode').on('click', Y.maas.node_add.showAddNodeWidget);
+
+ Y.one('#addnode').on('click', function(e) {
+ e.preventDefault();
+ Y.maas.node_add.showAddNodeWidget({targetNode: '#dashboard'});
+ });
// Setup TitleEditWidget.
var title_widget = new Y.maas.utils.TitleEditWidget(
@@ -70,5 +74,6 @@
<p id="retired-nodes" class="secondary medium space-top-none"></p>
<a href="#" id="addnode" class="button right space-top">Add node</a>
</div>
+ <div class="clear"></div>
</div>
{% endblock %}
=== modified file 'src/maasserver/templates/maasserver/node_list.html'
--- src/maasserver/templates/maasserver/node_list.html 2012-03-22 06:38:06 +0000
+++ src/maasserver/templates/maasserver/node_list.html 2012-04-02 06:48:18 +0000
@@ -14,7 +14,10 @@
<!--
YUI().use('maas.node_add', function (Y) {
Y.on('load', function() {
- Y.one('#addnode').on('click', Y.maas.node_add.showAddNodeWidget);
+ Y.one('#addnode').on('click', function(e) {
+ e.preventDefault();
+ Y.maas.node_add.showAddNodeWidget({targetNode: '#nodes'});
+ });
});
});
// -->
@@ -22,13 +25,14 @@
{% endblock %}
{% block content %}
- {% comment %}
- <!-- To be enabled when we have search functionality -->
- <form action="" method="get" class="block full-width inline-form">
- <input type="text" name="query" class="search-box" value="Search nodes" />
- </form>
- {% endcomment %}
- {% if node_list|length %}
+ <div id="nodes">
+ {% comment %}
+ <!-- To be enabled when we have search functionality -->
+ <form action="" method="get" class="block full-width inline-form">
+ <input type="text" name="query" class="search-box" value="Search nodes" />
+ </form>
+ {% endcomment %}
+ {% if node_list|length %}
<table class="list">
<thead>
<tr>
@@ -50,6 +54,7 @@
</tr>
{% endfor %}
</table>
- {% endif%}
- <a id="addnode" href="#" class="button right space-top">Add node</a>
+ {% endif%}
+ <a id="addnode" href="#" class="button right space-top">Add node</a>
+ </div>
{% endblock %}