Merge lp:~adeuring/launchpad/bug-903359 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Abel Deuring
Approved revision: no longer in the source branch.
Merged at revision: 14549
Proposed branch: lp:~adeuring/launchpad/bug-903359
Merge into: lp:launchpad
Diff against target: 309 lines (+112/-65)
5 files modified
lib/lp/app/javascript/ordering/ordering.js (+33/-28)
lib/lp/app/javascript/ordering/tests/test_orderby_widget.js (+43/-15)
lib/lp/bugs/browser/bugtask.py (+21/-21)
lib/lp/bugs/browser/tests/test_bugtask.py (+15/-0)
lib/lp/bugs/javascript/buglisting_utils.js (+0/-1)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-903359
Reviewer Review Type Date Requested Status
Deryck Hodge (community) Approve
Review via email: mp+86244@code.launchpad.net

Commit message

[r=deryck][bug=903359] configure ascending or descending default sort order for each OrderByBar sort option separately.

Description of the change

fault sort direction for each of the
sort options of a OrderByBar separately (bug 903359).

The implementation is simple: The constructor of the sort widget
has the parameter sort_keys, which was a sequences uf tuples
(name, title); I extended this to the tuple (name, title, direction).

The new field "direction" must have either the value "asc" or the value
"desc". (I considered to use a bool flag instead, but this would have
required more changes to the current implementation of OrderByBar,
which already uses these string -- and I wanted to finish this branch
today...)

I added the method OrderByWidget._setSortOrder() to remove duplicated
code in OrderByWidget.updateSortArrows() and OrderByWidget.updateSortArrows().

And I updated the constant SORT_KEYS in lp.bugs.browser.bugtask, which
the sort buttons to include the sort order.

Some choices of "asc" or "desc" in SORT_KEYS are candidates for
bikeshedding, especially: Should the default status sor direction
really be "asc", i.e. should the status "new" come first?

I don't want to discuss this here -- changing the default order for
each sort option requires only a one line change...

tests:

./bin/test app -vvt test_orderby_widget
./bin/test bugs -vvt lp.bugs.browser.tests.test_bugtask

no lint

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/javascript/ordering/ordering.js'
2--- lib/lp/app/javascript/ordering/ordering.js 2011-12-16 03:40:04 +0000
3+++ lib/lp/app/javascript/ordering/ordering.js 2011-12-19 14:16:28 +0000
4@@ -38,15 +38,15 @@
5 */
6 sort_keys: {
7 value: [
8- ['bugnumber', 'Bug number'],
9- ['bugtitle', 'Bug title'],
10- ['status', 'Status'],
11- ['importance', 'Importance'],
12- ['bug-heat-icons', 'Bug heat'],
13- ['package', 'Package name'],
14- ['milestone', 'Milestone'],
15- ['assignee', 'Assignee'],
16- ['bug-age', 'Bug age']
17+ ['bugnumber', 'Bug number', 'asc'],
18+ ['bugtitle', 'Bug title', 'asc'],
19+ ['status', 'Status', 'asc'],
20+ ['importance', 'Importance', 'desc'],
21+ ['bug-heat-icons', 'Bug heat', 'desc'],
22+ ['package', 'Package name', 'asc'],
23+ ['milestone', 'Milestone', 'asc'],
24+ ['assignee', 'Assignee', 'asc'],
25+ ['bug-age', 'Bug age', 'desc']
26 ]
27 },
28
29@@ -209,6 +209,20 @@
30 }
31 },
32
33+ _setSortOrder: function(value, arrow_span) {
34+ if (value === 'asc') {
35+ arrow_span.setContent(this.constructor.ASCENDING_ARROW);
36+ arrow_span.addClass('asc');
37+ arrow_span.removeClass('desc');
38+ this.set('sort_order', 'asc');
39+ } else {
40+ arrow_span.setContent(this.constructor.DESCENDING_ARROW);
41+ arrow_span.addClass('desc');
42+ arrow_span.removeClass('asc');
43+ this.set('sort_order', 'desc');
44+ }
45+ },
46+
47 /**
48 * Method used to update the arrows used in the display.
49 * This will also update the widget's attributes to match
50@@ -229,15 +243,9 @@
51 // Handle the case where the button clicked is the current
52 // active sort order. We change sort directions for it.
53 if (arrow_span.hasClass('desc')) {
54- arrow_span.setContent(this.constructor.ASCENDING_ARROW);
55- arrow_span.addClass('asc');
56- arrow_span.removeClass('desc');
57- this.set('sort_order', 'asc');
58+ this._setSortOrder('asc', arrow_span);
59 } else {
60- arrow_span.setContent(this.constructor.DESCENDING_ARROW);
61- arrow_span.addClass('desc');
62- arrow_span.removeClass('asc');
63- this.set('sort_order', 'desc');
64+ this._setSortOrder('desc', arrow_span);
65 }
66 } else {
67 // We have a different sort order clicked and need to
68@@ -251,9 +259,13 @@
69 var pre_click_sort_order = this.get('sort_order');
70 old_arrow_span.removeClass(pre_click_sort_order);
71 // Update current li span arrow and set new sort order.
72- arrow_span.addClass('asc');
73- this.set('sort_order', 'asc');
74- arrow_span.setContent(this.constructor.ASCENDING_ARROW);
75+ var sort_order;
76+ Y.each(this.get('sort_keys'), function(key_data) {
77+ if (key_data[0] === clicked_node_sort_key) {
78+ sort_order = key_data[2];
79+ }
80+ });
81+ this._setSortOrder(sort_order, arrow_span);
82 }
83 },
84
85@@ -335,15 +347,8 @@
86 li_node = Y.Node.create(li_html);
87 if (this.get('active') === id) {
88 li_node.addClass('active-sort');
89- li_node.one('span').addClass(this.get('sort_order'));
90 sort_order = this.get('sort_order');
91- if (sort_order === 'asc') {
92- li_node.one('span').setContent(
93- this.constructor.ASCENDING_ARROW);
94- } else if (sort_order === 'desc') {
95- li_node.one('span').setContent(
96- this.constructor.DESCENDING_ARROW);
97- }
98+ this._setSortOrder(sort_order, li_node.one('span'));
99 }
100 orderby_ul.appendChild(li_node);
101 li_nodes.push(li_node);
102
103=== modified file 'lib/lp/app/javascript/ordering/tests/test_orderby_widget.js'
104--- lib/lp/app/javascript/ordering/tests/test_orderby_widget.js 2011-12-08 13:23:00 +0000
105+++ lib/lp/app/javascript/ordering/tests/test_orderby_widget.js 2011-12-19 14:16:28 +0000
106@@ -83,9 +83,9 @@
107 test_user_supplied_sort_keys: function() {
108 // Call sites can supply their own sort keys to a widget.
109 var user_supplied_sort_keys = [
110- ['foo', 'Foo item'],
111- ['bar', 'Bar item'],
112- ['baz', 'Baz item']
113+ ['foo', 'Foo item', 'asc'],
114+ ['bar', 'Bar item', 'asc'],
115+ ['baz', 'Baz item', 'asc']
116 ];
117 this.orderby = new Y.lp.ordering.OrderByBar({
118 sort_keys: user_supplied_sort_keys});
119@@ -100,8 +100,8 @@
120 // created from sort keys, and the name should be used as
121 // a button display name in HTML.
122 var test_sort_keys = [
123- ['foo', 'Foo item'],
124- ['bar', 'Bar item']
125+ ['foo', 'Foo item', 'asc'],
126+ ['bar', 'Bar item', 'asc']
127 ];
128 this.makeSrcNode('test-div');
129 this.orderby = new Y.lp.ordering.OrderByBar({
130@@ -187,8 +187,8 @@
131 // This should fail because we do not allow
132 // a "active" value not found in sort_keys.
133 var test_sort_keys = [
134- ['foo', 'Foo item'],
135- ['bar', 'Bar item']
136+ ['foo', 'Foo item', 'asc'],
137+ ['bar', 'Bar item', 'asc']
138 ];
139 this.orderby = new Y.lp.ordering.OrderByBar({
140 sort_keys: test_sort_keys,
141@@ -212,8 +212,8 @@
142 // happen.
143 this.makeSrcNode('test-div');
144 var test_sort_keys = [
145- ['foo', 'Foo item'],
146- ['bar', 'Bar item']
147+ ['foo', 'Foo item', 'asc'],
148+ ['bar', 'Bar item', 'asc']
149 ];
150 this.orderby = new Y.lp.ordering.OrderByBar({
151 srcNode: Y.one('#test-div'),
152@@ -223,8 +223,10 @@
153 });
154 this.orderby.render();
155 var foo_node = Y.one('#sort-foo');
156- var expected_starting_text = '<span class="sprite order-ascending"></span>';
157- var expected_ending_text = '<span class="sprite order-descending"></span>';
158+ var expected_starting_text =
159+ '<span class="sprite order-ascending"></span>';
160+ var expected_ending_text =
161+ '<span class="sprite order-descending"></span>';
162 Assert.areEqual(
163 expected_starting_text, foo_node.one('span').get('innerHTML'));
164 Assert.isTrue(foo_node.one('span').hasClass('asc'));
165@@ -240,8 +242,8 @@
166 // change should happen.
167 this.makeSrcNode('test-div');
168 var test_sort_keys = [
169- ['foo', 'Foo item'],
170- ['bar', 'Bar item']
171+ ['foo', 'Foo item', 'asc'],
172+ ['bar', 'Bar item', 'asc']
173 ];
174 this.orderby = new Y.lp.ordering.OrderByBar({
175 srcNode: Y.one('#test-div'),
176@@ -261,6 +263,32 @@
177 Assert.isFalse(Y.one('#sort-foo').one('span').hasClass('desc'));
178 },
179
180+ test_click_different_sort_arrows_change_default_order: function() {
181+ // A newly active sort button has the order as specified by
182+ // the constructor parameter sort_keys.
183+ this.makeSrcNode('test-div');
184+ var test_sort_keys = [
185+ ['foo', 'Foo item', 'asc'],
186+ ['bar', 'Bar item', 'desc'],
187+ ['baz', 'Baz item', 'asc']
188+ ];
189+ this.orderby = new Y.lp.ordering.OrderByBar({
190+ srcNode: Y.one('#test-div'),
191+ sort_keys: test_sort_keys,
192+ active: 'foo',
193+ sort_order: 'asc'
194+ });
195+ this.orderby.render();
196+ var bar_node = Y.one('#sort-bar');
197+ bar_node.simulate('click');
198+ Assert.isTrue(bar_node.one('span').hasClass('desc'));
199+ Assert.areEqual('desc', this.orderby.get('sort_order'));
200+ var baz_node = Y.one('#sort-baz');
201+ baz_node.simulate('click');
202+ Assert.isTrue(baz_node.one('span').hasClass('asc'));
203+ Assert.areEqual('asc', this.orderby.get('sort_order'));
204+ },
205+
206 test_sort_clause_default: function() {
207 // sort_clause defaults to "importance".
208 this.orderby = new Y.lp.ordering.OrderByBar();
209@@ -275,8 +303,8 @@
210 // a URL.
211 this.makeSrcNode('test-div');
212 var test_sort_keys = [
213- ['foo', 'Foo item'],
214- ['bar', 'Bar item']
215+ ['foo', 'Foo item', 'asc'],
216+ ['bar', 'Bar item', 'asc']
217 ];
218 this.orderby = new Y.lp.ordering.OrderByBar({
219 srcNode: Y.one('#test-div'),
220
221=== modified file 'lib/lp/bugs/browser/bugtask.py'
222--- lib/lp/bugs/browser/bugtask.py 2011-12-18 03:35:49 +0000
223+++ lib/lp/bugs/browser/bugtask.py 2011-12-19 14:16:28 +0000
224@@ -2494,27 +2494,27 @@
225 # All sort orders supported by BugTaskSet.search() and a title for
226 # them.
227 SORT_KEYS = [
228- ('importance', 'Importance'),
229- ('status', 'Status'),
230- ('id', 'Bug number'),
231- ('title', 'Bug title'),
232- ('targetname', 'Package/Project/Series name'),
233- ('milestone_name', 'Milestone'),
234- ('date_last_updated', 'Date bug last updated'),
235- ('assignee', 'Assignee'),
236- ('reporter', 'Reporter'),
237- ('datecreated', 'Bug age'),
238- ('tag', 'Bug Tags'),
239- ('heat', 'Bug heat'),
240- ('date_closed', 'Date bug closed'),
241- ('dateassigned', 'Date when the bug task was assigned'),
242- ('number_of_duplicates', 'Number of duplicates'),
243- ('latest_patch_uploaded', 'Date latest patch uploaded'),
244- ('message_count', 'Number of comments'),
245- ('milestone', 'Milestone ID'),
246- ('specification', 'Linked blueprint'),
247- ('task', 'Bug task ID'),
248- ('users_affected_count', 'Number of affected users'),
249+ ('importance', 'Importance', 'desc'),
250+ ('status', 'Status', 'asc'),
251+ ('id', 'Bug number', 'desc'),
252+ ('title', 'Bug title', 'asc'),
253+ ('targetname', 'Package/Project/Series name', 'asc'),
254+ ('milestone_name', 'Milestone', 'asc'),
255+ ('date_last_updated', 'Date bug last updated', 'desc'),
256+ ('assignee', 'Assignee', 'asc'),
257+ ('reporter', 'Reporter', 'asc'),
258+ ('datecreated', 'Bug age', 'desc'),
259+ ('tag', 'Bug Tags', 'asc'),
260+ ('heat', 'Bug heat', 'desc'),
261+ ('date_closed', 'Date bug closed', 'desc'),
262+ ('dateassigned', 'Date when the bug task was assigned', 'desc'),
263+ ('number_of_duplicates', 'Number of duplicates', 'desc'),
264+ ('latest_patch_uploaded', 'Date latest patch uploaded', 'desc'),
265+ ('message_count', 'Number of comments', 'desc'),
266+ ('milestone', 'Milestone ID', 'desc'),
267+ ('specification', 'Linked blueprint', 'asc'),
268+ ('task', 'Bug task ID', 'desc'),
269+ ('users_affected_count', 'Number of affected users', 'desc'),
270 ]
271
272
273
274=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
275--- lib/lp/bugs/browser/tests/test_bugtask.py 2011-12-15 18:55:46 +0000
276+++ lib/lp/bugs/browser/tests/test_bugtask.py 2011-12-19 14:16:28 +0000
277@@ -2263,6 +2263,21 @@
278 "keys present in JSON cache but not defined: %r"
279 % (valid_keys - json_sort_keys, json_sort_keys - valid_keys))
280
281+ def test_sort_keys_in_json_cache_data(self):
282+ # The entry 'sort_keys' in the JSON cache of a search listing
283+ # view is a sequence of 3-tuples (name, title, order), where
284+ # order is one of the string 'asc' or 'desc'.
285+ with dynamic_listings():
286+ view = self.makeView()
287+ cache = IJSONRequestCache(view.request)
288+ json_sort_keys = cache.objects['sort_keys']
289+ for key in json_sort_keys:
290+ self.assertEqual(
291+ 3, len(key), 'Invalid key length: %r' % (key, ))
292+ self.assertTrue(
293+ key[2] in ('asc', 'desc'),
294+ 'Invalid order value: %r' % (key, ))
295+
296
297 class TestBugTaskExpirableListingView(BrowserTestCase):
298 """Test BugTaskExpirableListingView."""
299
300=== modified file 'lib/lp/bugs/javascript/buglisting_utils.js'
301--- lib/lp/bugs/javascript/buglisting_utils.js 2011-12-15 15:11:50 +0000
302+++ lib/lp/bugs/javascript/buglisting_utils.js 2011-12-19 14:16:28 +0000
303@@ -344,7 +344,6 @@
304 Y.each(orderbybar.always_display, function(sort_key) {
305 orderby_visibility[sort_key] = true;
306 });
307- Y.log(data_visibility);
308 orderbybar.updateVisibility(orderby_visibility);
309 }
310