Merge lp:~blake-rouse/maas/fix-multi-bugs-node-listing into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 3792
Proposed branch: lp:~blake-rouse/maas/fix-multi-bugs-node-listing
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 342 lines (+145/-36)
5 files modified
src/maasserver/static/js/angular/controllers/node_details.js (+5/-0)
src/maasserver/static/js/angular/controllers/nodes_list.js (+29/-2)
src/maasserver/static/js/angular/controllers/tests/test_node_details.js (+8/-0)
src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js (+67/-1)
src/maasserver/static/partials/nodes-list.html (+36/-33)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-multi-bugs-node-listing
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+255415@code.launchpad.net

Commit message

Fix selection of node on click to go to node details. Fix inconsistent button text on node listing. Fix query parameter to be used by NodesListController. Fix clicking FQDN/MAC doesn't sort until second click.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good. FWIW, I hate that we're using the weird "Installable" vocable instead of the much precise "Node" that we've been using all this time.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/static/js/angular/controllers/node_details.js'
2--- src/maasserver/static/js/angular/controllers/node_details.js 2015-04-02 18:37:30 +0000
3+++ src/maasserver/static/js/angular/controllers/node_details.js 2015-04-08 13:39:05 +0000
4@@ -612,6 +612,11 @@
5
6 // Return true when the value in nameHeader is invalid.
7 $scope.editNameInvalid = function() {
8+ // Not invalid unless editing.
9+ if(!$scope.nameHeader.editing) {
10+ return false;
11+ }
12+
13 // The value cannot be blank.
14 var value = $scope.nameHeader.value;
15 if(value.length === 0) {
16
17=== modified file 'src/maasserver/static/js/angular/controllers/nodes_list.js'
18--- src/maasserver/static/js/angular/controllers/nodes_list.js 2015-04-03 18:40:15 +0000
19+++ src/maasserver/static/js/angular/controllers/nodes_list.js 2015-04-08 13:39:05 +0000
20@@ -5,9 +5,9 @@
21 */
22
23 angular.module('MAAS').controller('NodesListController', [
24- '$scope', '$rootScope', '$location', 'NodesManager', 'DevicesManager',
25+ '$scope', '$rootScope', '$routeParams', 'NodesManager', 'DevicesManager',
26 'GeneralManager', 'ManagerHelperService', 'SearchService', 'ZonesManager',
27- function($scope, $rootScope, $location, NodesManager, DevicesManager,
28+ function($scope, $rootScope, $routeParams, NodesManager, DevicesManager,
29 GeneralManager, ManagerHelperService, SearchService, ZonesManager) {
30
31 // Mapping of device.ip_assignment to viewable text.
32@@ -176,6 +176,12 @@
33 $scope.currentpage = tab;
34 };
35
36+ // Clear the search bar.
37+ $scope.clearSearch = function(tab) {
38+ $scope.tabs[tab].search = "";
39+ shouldClearAction(tab);
40+ };
41+
42 // Mark a node as selected or unselected.
43 $scope.toggleChecked = function(node, tab) {
44 if($scope.tabs[tab].manager.isSelected(node.system_id)) {
45@@ -251,6 +257,21 @@
46 shouldClearAction(tab);
47 };
48
49+ // Sorts the table by predicate.
50+ $scope.sortTable = function(predicate, tab) {
51+ $scope.tabs[tab].predicate = predicate;
52+ $scope.tabs[tab].reverse = !$scope.tabs[tab].reverse;
53+ };
54+
55+ // Sets the viewable column or sorts.
56+ $scope.selectColumnOrSort = function(predicate, tab) {
57+ if($scope.tabs[tab].column !== predicate) {
58+ $scope.tabs[tab].column = predicate;
59+ } else {
60+ $scope.sortTable(predicate, tab);
61+ }
62+ };
63+
64 // Return True if the node supports the action.
65 $scope.supportsAction = function(node, tab) {
66 if(!$scope.tabs[tab].actionOption) {
67@@ -385,4 +406,10 @@
68 $scope.$on("$destroy", function() {
69 GeneralManager.stopPolling("osinfo");
70 });
71+
72+ // Set the query if the present in $routeParams.
73+ var query = $routeParams.query;
74+ if(angular.isString(query)) {
75+ $scope.tabs.nodes.search = query;
76+ }
77 }]);
78
79=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details.js'
80--- src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2015-04-03 21:43:55 +0000
81+++ src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2015-04-08 13:39:05 +0000
82@@ -1007,8 +1007,16 @@
83
84 describe("editNameInvalid", function() {
85
86+ it("returns false if not editing", function() {
87+ var controller = makeController();
88+ $scope.nameHeader.editing = false;
89+ $scope.nameHeader.value = "abc_invalid.local";
90+ expect($scope.editNameInvalid()).toBe(false);
91+ });
92+
93 it("returns true for bad values", function() {
94 var controller = makeController();
95+ $scope.nameHeader.editing = true;
96 var values = [
97 {
98 input: "aB0-z",
99
100=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js'
101--- src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js 2015-04-03 21:43:55 +0000
102+++ src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js 2015-04-08 13:39:05 +0000
103@@ -10,12 +10,13 @@
104 beforeEach(module("MAAS"));
105
106 // Grab the needed angular pieces.
107- var $controller, $rootScope, $scope, $q;
108+ var $controller, $rootScope, $scope, $q, $routeParams;
109 beforeEach(inject(function($injector) {
110 $controller = $injector.get("$controller");
111 $rootScope = $injector.get("$rootScope");
112 $scope = $rootScope.$new();
113 $q = $injector.get("$q");
114+ $routeParams = {};
115 }));
116
117 // Load the NodesManager, DevicesManager, GeneralManager,
118@@ -60,6 +61,7 @@
119 return $controller("NodesListController", {
120 $scope: $scope,
121 $rootScope: $rootScope,
122+ $routeParams: $routeParams,
123 NodesManager: NodesManager,
124 DevicesManager: DevicesManager,
125 ManagerHelperService: ManagerHelperService,
126@@ -129,6 +131,14 @@
127 [NodesManager, DevicesManager, GeneralManager, ZonesManager]);
128 });
129
130+ it("sets nodes search from $routeParams.query",
131+ function() {
132+ var query = makeName("query");
133+ $routeParams.query = query;
134+ var controller = makeController();
135+ expect($scope.tabs.nodes.search).toBe(query);
136+ });
137+
138 describe("toggleTab", function() {
139
140 it("sets $rootScope.title", function() {
141@@ -197,6 +207,25 @@
142
143 describe("tab(" + tab + ")", function() {
144
145+ describe("clearSearch", function() {
146+
147+ it("sets search to empty string", function() {
148+ var controller = makeController();
149+ $scope.tabs[tab].search = makeName("search");
150+ $scope.clearSearch(tab);
151+ expect($scope.tabs[tab].search).toBe("");
152+ });
153+
154+ it("resets actionOption", function() {
155+ var controller = makeController();
156+ $scope.tabs[tab].actionOption = {
157+ name: "deploy"
158+ };
159+ $scope.clearSearch(tab);
160+ expect($scope.tabs[tab].actionOption).toBeNull();
161+ });
162+ });
163+
164 describe("toggleChecked", function() {
165
166 var controller, object, tabObj;
167@@ -444,6 +473,43 @@
168 });
169 });
170
171+ describe("sortTable", function() {
172+
173+ it("sets predicate", function() {
174+ var controller = makeController();
175+ var predicate = makeName('predicate');
176+ $scope.sortTable(predicate, tab);
177+ expect($scope.tabs[tab].predicate).toBe(predicate);
178+ });
179+
180+ it("reverses reverse", function() {
181+ var controller = makeController();
182+ $scope.tabs[tab].reverse = true;
183+ $scope.sortTable(makeName('predicate'), tab);
184+ expect($scope.tabs[tab].reverse).toBe(false);
185+ });
186+ });
187+
188+ describe("selectColumnOrSort", function() {
189+
190+ it("sets column if different", function() {
191+ var controller = makeController();
192+ var column = makeName('column');
193+ $scope.selectColumnOrSort(column, tab);
194+ expect($scope.tabs[tab].column).toBe(column);
195+ });
196+
197+ it("calls sortTable if column already set", function() {
198+ var controller = makeController();
199+ var column = makeName('column');
200+ $scope.tabs[tab].column = column;
201+ spyOn($scope, "sortTable");
202+ $scope.selectColumnOrSort(column, tab);
203+ expect($scope.sortTable).toHaveBeenCalledWith(
204+ column, tab);
205+ });
206+ });
207+
208 describe("supportsAction", function() {
209
210 it("returns true if actionOption is null", function() {
211
212=== modified file 'src/maasserver/static/partials/nodes-list.html'
213--- src/maasserver/static/partials/nodes-list.html 2015-04-03 15:09:25 +0000
214+++ src/maasserver/static/partials/nodes-list.html 2015-04-08 13:39:05 +0000
215@@ -390,7 +390,9 @@
216 type="search" placeholder="Search nodes" class="search__input"
217 data-ng-model="tabs.nodes.search" data-ng-change="updateFilters('nodes')"
218 data-ng-class="{ error: !tabs.nodes.searchValid }" />
219- <input type="submit" class="search__submit" />
220+ <input type="submit" class="search__submit"
221+ data-ng-class="{ close: tabs.nodes.search.length > 0 }"
222+ data-ng-click="clearSearch('nodes')" />
223 </div>
224 <table class="table-listing " id="nodes-listing">
225 <thead>
226@@ -402,41 +404,41 @@
227 </th>
228 <th colspan="2" class="table-listing__header fixed">
229 <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
230- <a href="" data-ng-click="predicate='fqdn'; tabs.nodes.reverse=!tabs.nodes.reverse; tabs.nodes.column='fqdn'">
231+ <a href="" data-ng-click="selectColumnOrSort('fqdn', 'nodes')">
232 <acronym title="Fully Qualified Domain Name">FQDN</acronym>
233 </a> |
234- <a href="" data-ng-click="predicate='pxe_mac'; tabs.nodes.reverse=!tabs.nodes.reverse; tabs.nodes.column='pxe_mac'">
235+ <a href="" data-ng-click="selectColumnOrSort('pxe_mac', 'nodes')">
236 <acronym title="Media Access Control addresses">MAC</acronym>
237 </a>
238 </th>
239 <th class="align-center">
240 <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
241- <a href="" class="align-center" data-ng-click="tabs.nodes.predicate='power_state'; tabs.nodes.reverse=!tabs.nodes.reverse">Power</a>
242- </th>
243- <th>
244- <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
245- <a href="" data-ng-click="tabs.nodes.predicate='status'; tabs.nodes.reverse=!tabs.nodes.reverse">Status</a>
246- </th>
247- <th>
248- <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
249- <a href="" data-ng-click="tabs.nodes.predicate='owner'; tabs.nodes.reverse=!tabs.nodes.reverse">Owner</a>
250+ <a href="" class="align-center" data-ng-click="sortTable('power_state', 'nodes')">Power</a>
251+ </th>
252+ <th>
253+ <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
254+ <a href="" data-ng-click="sortTable('status', 'nodes')">Status</a>
255+ </th>
256+ <th>
257+ <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
258+ <a href="" data-ng-click="sortTable('owner', 'nodes')">Owner</a>
259 </th>
260
261 <th class="align-right">
262 <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
263- <a href="" data-ng-click="tabs.nodes.predicate='cpu_count'; tabs.nodes.reverse=!tabs.nodes.reverse">Cores</a>
264- </th>
265- <th class="align-right">
266- <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
267- <a href="" data-ng-click="tabs.nodes.predicate='memory'; tabs.nodes.reverse=!tabs.nodes.reverse">RAM (GB)</a>
268- </th>
269- <th class="align-right">
270- <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
271- <a href="" data-ng-click="tabs.nodes.predicate='disks'; tabs.nodes.reverse=!tabs.nodes.reverse">Disks</a>
272- </th>
273- <th class="align-right">
274- <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
275- <a href="" data-ng-click="tabs.nodes.predicate='storage'; tabs.nodes.reverse=!tabs.nodes.reverse">Storage (GB)</a>
276+ <a href="" data-ng-click="sortTable('cpu_count', 'nodes')">Cores</a>
277+ </th>
278+ <th class="align-right">
279+ <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
280+ <a href="" data-ng-click="sortTable('memory', 'nodes')">RAM (GB)</a>
281+ </th>
282+ <th class="align-right">
283+ <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
284+ <a href="" data-ng-click="sortTable('disks', 'nodes')">Disks</a>
285+ </th>
286+ <th class="align-right">
287+ <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
288+ <a href="" data-ng-click="sortTable('storage', 'nodes')">Storage (GB)</a>
289 </th>
290 </tr>
291 </thead>
292@@ -444,8 +446,7 @@
293 <!-- XXX blake_r 2015-02-18 - Need to add e2e test. This really needs lots of tests. -->
294 <tr class="table-listing__row table-listing-row"
295 data-ng-repeat="node in tabs.nodes.filtered_items = (nodes | nodesFilter:tabs.nodes.search | orderBy:tabs.nodes.predicate:tabs.nodes.reverse) track by node.system_id"
296- data-ng-class="{ error: !supportsAction(node, 'nodes'), selected: node.$selected }"
297- data-ng-click="toggleChecked(node, 'nodes')">
298+ data-ng-class="{ error: !supportsAction(node, 'nodes'), selected: node.$selected }">
299 <td>
300 <!-- XXX blake_r 2015-02-18 - Need to add e2e test. -->
301 <input class="checkbox" type="checkbox" data-ng-click="toggleChecked(node, 'nodes')" data-ng-model="node.$selected" id="{$ node.fqdn $}" />
302@@ -482,7 +483,9 @@
303 type="search" placeholder="Search devices" class="search__input"
304 data-ng-model="tabs.devices.search" data-ng-change="updateFilters('devices')"
305 data-ng-class="{ error: !tabs.devices.searchValid }" />
306- <input type="submit" class="search__submit" />
307+ <input type="submit" class="search__submit"
308+ data-ng-class="{ close: tabs.devices.search.length > 0 }"
309+ data-ng-click="clearSearch('devices')" />
310 </div>
311 <table class="table-listing" id="nodes-listing">
312 <thead>
313@@ -494,24 +497,24 @@
314 </th>
315 <th class="table-listing__header t24">
316 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
317- <a href="" data-ng-click="tabs.devices.predicate='fqdn'; tabs.devices.reverse=!tabs.devices.reverse;">
318+ <a href="" data-ng-click="sortTable('fqdn', 'devices')">
319 <acronym title="Fully Qualified Domain Name">FQDN</acronym>
320 </a>
321 </th>
322 <th class="table-listing__header t25">
323- <a href="" data-ng-click="tabs.devices.predicate='primary_mac'; tabs.devices.reverse=!tabs.devices.reverse;">
324+ <a href="" data-ng-click="sortTable('primary_mac', 'devices')">
325 <acronym title="Media Access Control addresses">MAC</acronym>
326 </a>
327 </th>
328 <th class="table-listing__header t15">
329- <a href="" data-ng-click="tabs.devices.predicate='ip_assignment'; tabs.devices.reverse=!tabs.devices.reverse">IP assignment</a>
330+ <a href="" data-ng-click="sortTable('ip_assignment', 'devices')">IP assignment</a>
331 </th>
332 <th class="table-listing__header t20">
333- <a href="" data-ng-click="tabs.devices.predicate='ip_address'; tabs.devices.reverse=!tabs.devices.reverse">IP</a>
334+ <a href="" data-ng-click="sortTable('ip_address', 'devices')">IP</a>
335 </th>
336 <th class="table-listing__header t15">
337 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
338- <a href="" data-ng-click="tabs.devices.predicate='owner'; tabs.devices.reverse=!tabs.devices.reverse">Owner</a>
339+ <a href="" data-ng-click="sortTable('owner', 'devices')">Owner</a>
340 </th>
341 </tr>
342 </thead>