Merge lp:~jcsackett/launchpad/form-macros-frighten-me into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 13485
Proposed branch: lp:~jcsackett/launchpad/form-macros-frighten-me
Merge into: lp:launchpad
Diff against target: 175 lines (+25/-43)
7 files modified
lib/lp/answers/browser/tests/test_questiontarget.py (+2/-3)
lib/lp/app/javascript/picker/tests/test_picker_patcher.js (+3/-1)
lib/lp/app/widgets/popup.py (+7/-5)
lib/lp/app/widgets/templates/form-picker-macros.pt (+3/-13)
lib/lp/app/widgets/tests/test_popup.py (+6/-15)
lib/lp/blueprints/browser/tests/test_specificationtarget.py (+2/-3)
lib/lp/bugs/browser/tests/test_bugs.py (+2/-3)
To merge this branch: bzr merge lp:~jcsackett/launchpad/form-macros-frighten-me
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+68277@code.launchpad.net

Commit message

[r=benji][bug=799847] Cleans up form-picker-macros

Description of the change

Summary
=======
The form-picker-macros.pt template provides the means of wiring pickers into the launchpad webui. While it works, it's ugly--javascript is repeated for every picker element that could be extracted into our javascript libraries, and a lot of manual config is assembled from the view properties.

This branch cleans this up to the extent possible.

Pre-implementation notes
========================
Benji York identified the problem in an earlier review; Aaron Bentley expanded on it in another pickers review, pointing out that the means of passing in all the various config data was likely more error prone since we were setting "booleans" in python code that were actually strings to be evaluated as javascript booleans.

Ian helped flesh out the final config ideas.

Proposed fix
============
The intial idea involved moving the make_picker function into the existing javascript files, and using the JSONRequestCache to get config data. It turns out that the popup.py widgets can't put values into JSONRequestCache (or if they can, it's sufficiently roundabout as to constitute a bad hack).

Implementation details
======================
The majority of the properties in the view are now assembled into a config property and a json_config propery, which uses simplejson to dump them to javascript. This approach was implemented simulataneously in this branch and in another that beat it to devel, so the majority of the work doesn't show up in the diff (being part of the tree). Please keep an eye open for bad conflict resolution artifacts on my part, since the collision was pretty massive. I believe I caught everything, and tests/UI all work as expected, but another set of eyes wouldn't hurt.

Because simplejson is now used to convert the variables into a javascript dictionary, the properties that were javascript style booleans can now be converted back into python booleans. They're unlikely to be used outside of the widget, but view code that does so will no longer be dangerous.

make_picker now exists in the picker files as initially planned.

Tests
=====

bin/test -vvct test_popup
bin/test -vvc --layer=YUI

QA
==

Checkout a personpicker instance (e.g. on +edit-people). Everything should work as before this branch.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/widgets/templates/form-picker-macros.pt
  lib/lp/answers/browser/tests/test_questiontarget.py
  lib/lp/app/javascript/picker/picker_patcher.js
  lib/lp/bugs/browser/tests/test_bugs.py
  lib/lp/blueprints/browser/tests/test_specificationtarget.py
  lib/lp/app/widgets/popup.py

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. I'm glad you're working on this.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/answers/browser/tests/test_questiontarget.py'
2--- lib/lp/answers/browser/tests/test_questiontarget.py 2011-06-17 15:09:15 +0000
3+++ lib/lp/answers/browser/tests/test_questiontarget.py 2011-07-21 18:17:45 +0000
4@@ -218,8 +218,7 @@
5 self.assertIsNot(
6 None, content.find(True, id=target_widget.show_widget_id))
7 text = str(content)
8- picker_script = (
9- "Y.lp.app.picker.create('DistributionOrProductOrProjectGroup'")
10- self.assertIn(picker_script, text)
11+ picker_vocab = "DistributionOrProductOrProjectGroup"
12+ self.assertIn(picker_vocab, text)
13 focus_script = "setFocusByName('field.search_text')"
14 self.assertIn(focus_script, text)
15
16=== modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.js'
17--- lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-19 12:22:03 +0000
18+++ lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-21 18:17:45 +0000
19@@ -313,8 +313,10 @@
20 "selected_value_metadata": params.selected_value_metadata,
21 "assign_me_text": "Assign Moi",
22 "remove_person_text": "Remove someone",
23- "remove_team_text": "Remove some team"
24+ "remove_team_text": "Remove some team",
25+ "vocabular_name": this.vocabulary
26 };
27+
28 this.picker = Y.lp.app.picker.addPickerPatcher(
29 this.vocabulary,
30 "foo/bar",
31
32=== modified file 'lib/lp/app/widgets/popup.py'
33--- lib/lp/app/widgets/popup.py 2011-07-19 03:27:34 +0000
34+++ lib/lp/app/widgets/popup.py 2011-07-21 18:17:45 +0000
35@@ -32,8 +32,8 @@
36 # Provide default values for the following properties in case someone
37 # creates a vocab picker for a person instead of using the derived
38 # PersonPicker.
39- show_assign_me_button = 'false'
40- show_remove_button = 'false'
41+ show_assign_me_button = False
42+ show_remove_button = False
43 assign_me_text = 'Pick me'
44 remove_person_text = 'Remove person'
45 remove_team_text = 'Remove team'
46@@ -123,7 +123,9 @@
47 remove_person_text=self.remove_person_text,
48 remove_team_text=self.remove_team_text,
49 show_remove_button=self.show_remove_button,
50- show_assign_me_button=self.show_assign_me_button)
51+ show_assign_me_button=self.show_assign_me_button,
52+ vocabulary_name=self.vocabulary_name,
53+ input_element=self.input_id)
54
55 @property
56 def json_config(self):
57@@ -189,8 +191,8 @@
58 class PersonPickerWidget(VocabularyPickerWidget):
59
60 include_create_team_link = False
61- show_assign_me_button = 'true'
62- show_remove_button = 'true'
63+ show_assign_me_button = True
64+ show_remove_button = True
65
66 @property
67 def picker_type(self):
68
69=== modified file 'lib/lp/app/widgets/templates/form-picker-macros.pt'
70--- lib/lp/app/widgets/templates/form-picker-macros.pt 2011-07-20 02:53:15 +0000
71+++ lib/lp/app/widgets/templates/form-picker-macros.pt 2011-07-21 18:17:45 +0000
72@@ -35,19 +35,9 @@
73 return;
74 }
75
76- // The vocabulary picker, created when used for the first time.
77- function make_picker() {
78- var config = ${view/json_config};
79- if (config.picker_type == 'person') {
80- config.show_assign_me_button = ${view/show_assign_me_button}
81- config.show_remove_button = ${view/show_remove_button}
82- }
83- var picker = Y.lp.app.picker.create('${view/vocabulary_name}',
84- config,
85- '${view/input_id}');
86- return picker;
87- }
88 var picker = null;
89+ var config = ${view/json_config};
90+ var vocabulary = config.vocabulary_name;
91 Y.on('domready', function(e) {
92 // Sort out the Choose... link.
93 var show_widget_node = Y.one('#${view/show_widget_id}');
94@@ -57,7 +47,7 @@
95 show_widget_node.get('parentNode').removeClass('unseen');
96 show_widget_node.on('click', function (e) {
97 if (picker === null) {
98- picker = make_picker();
99+ picker = Y.lp.app.picker.create(vocabulary, config);
100 }
101 picker.show();
102 e.preventDefault();
103
104=== modified file 'lib/lp/app/widgets/tests/test_popup.py'
105--- lib/lp/app/widgets/tests/test_popup.py 2011-07-20 02:53:15 +0000
106+++ lib/lp/app/widgets/tests/test_popup.py 2011-07-21 18:17:45 +0000
107@@ -71,13 +71,8 @@
108 'field.test_valid.item', picker_widget.input_id)
109 self.assertIsNone(picker_widget.extra_no_results_message)
110 markup = picker_widget()
111- self.assertTextMatchesExpressionIgnoreWhitespace("""\
112- .*
113- var picker = Y\\.lp\\.app\\.picker\\.create\\('ValidTeamOwner',
114- config,
115- 'field\\.test_valid.item'\\);
116- .*
117- """, markup)
118+ self.assertIn("Y.lp.app.picker.create", markup)
119+ self.assertIn('ValidTeamOwner', markup)
120
121 def test_widget_fieldname_with_invalid_html_chars(self):
122 # Check the picker widget is correctly set up for a field which has a
123@@ -126,18 +121,14 @@
124 # A vocabulary widget does not show the extra buttons by default.
125 picker_widget = VocabularyPickerWidget(
126 bound_field, self.vocabulary, self.request)
127- self.assertEqual('false',
128- picker_widget.config['show_assign_me_button'])
129- self.assertEqual('false',
130- picker_widget.config['show_remove_button'])
131+ self.assertFalse(picker_widget.config['show_assign_me_button'])
132+ self.assertFalse(picker_widget.config['show_remove_button'])
133
134 # A person picker widget does show them by default.
135 person_picker_widget = PersonPickerWidget(
136 bound_field, self.vocabulary, self.request)
137- self.assertEqual('true',
138- person_picker_widget.config['show_assign_me_button'])
139- self.assertEqual('true',
140- person_picker_widget.config['show_remove_button'])
141+ self.assertTrue(person_picker_widget.config['show_assign_me_button'])
142+ self.assertTrue(person_picker_widget.config['show_remove_button'])
143
144 def test_widget_personvalue_meta(self):
145 # The person picker has the correct meta value for a person value.
146
147=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
148--- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2011-06-17 15:09:15 +0000
149+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2011-07-21 18:17:45 +0000
150@@ -299,8 +299,7 @@
151 self.assertIsNot(
152 None, content.find(True, id=target_widget.show_widget_id))
153 text = str(content)
154- picker_script = (
155- "Y.lp.app.picker.create('DistributionOrProductOrProjectGroup'")
156- self.assertIn(picker_script, text)
157+ picker_vocab = 'DistributionOrProductOrProjectGroup'
158+ self.assertIn(picker_vocab, text)
159 focus_script = "setFocusByName('field.search_text')"
160 self.assertIn(focus_script, text)
161
162=== modified file 'lib/lp/bugs/browser/tests/test_bugs.py'
163--- lib/lp/bugs/browser/tests/test_bugs.py 2011-06-17 15:09:15 +0000
164+++ lib/lp/bugs/browser/tests/test_bugs.py 2011-07-21 18:17:45 +0000
165@@ -86,8 +86,7 @@
166 self.assertIsNot(
167 None, content.find(True, id=target_widget.show_widget_id))
168 text = str(content)
169- picker_script = (
170- "Y.lp.app.picker.create('DistributionOrProductOrProjectGroup'")
171- self.assertIn(picker_script, text)
172+ picker_vocab = "DistributionOrProductOrProjectGroup"
173+ self.assertIn(picker_vocab, text)
174 focus_script = "setFocusByName('field.searchtext')"
175 self.assertIn(focus_script, text)