Merge lp:~salgado/launchpad/bug-482235 into lp:launchpad
- bug-482235
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~salgado/launchpad/bug-482235 |
Merge into: | lp:launchpad |
Diff against target: |
270 lines (+87/-33) 10 files modified
Makefile (+0/-3) lib/canonical/launchpad/javascript/bugs/bugtask-index.js (+6/-6) lib/canonical/launchpad/javascript/code/codereview.js (+5/-7) lib/canonical/launchpad/javascript/lp/picker.js (+11/-9) lib/canonical/launchpad/javascript/registry/team.js (+2/-1) lib/canonical/launchpad/windmill/testing/lpuser.py (+2/-0) lib/canonical/widgets/popup.py (+1/-1) lib/canonical/widgets/templates/vocabulary-picker.js.template (+4/-5) lib/lp/registry/windmill/tests/test_person_picker.py (+55/-0) versions.cfg (+1/-1) |
To merge this branch: | bzr merge lp:~salgado/launchpad/bug-482235 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Māris Fogels (community) | Approve | ||
Canonical Launchpad Engineering | Pending | ||
Review via email: mp+17585@code.launchpad.net |
Commit message
Description of the change
Guilherme Salgado (salgado) wrote : | # |
Māris Fogels (mars) wrote : | # |
Hi Salgado,
Your changes look good, and thanks for updating lazr-js.
I see one coding convention that needs fixing, and had some small questions about implementation details.
• line 62: By convention, the optional 'save' function should be a key in the config dictionary. Only the simplest of functions use optional positional args in JavaScript, as getting around them is a pain (unlike in Python).
• line 78: Might be better to use Y.Lang.isFunction()
• line 116: Does YUI understand field IDs with dots in them yet? If it does, then the input_element string construction can be greatly simplified using a '#'
• line 163: What is the 'shadow' element? Some invisible element in the DOM? Just asking to make sure that 'shadow' is a behavioural object, not a presentation object :)
• line 170: Why do you have a hard sleep here?
• Regarding the JSLint error: should we rename that file to .js.pt or .js.template, as it isn't a valid JavaScript file?
Maris
Guilherme Salgado (salgado) wrote : | # |
On Mon, 2010-01-18 at 20:22 +0000, Māris Fogels wrote:
> Review: Needs Fixing
> Hi Salgado,
>
> Your changes look good, and thanks for updating lazr-js.
Thanks for the review. In the future, would you mind including the diff
in your review and adding your comments around the code you're referring
to? That makes the conversation a lot smoother as we don't have to keep
going back to the diff for context.
>
> I see one coding convention that needs fixing, and had some small
> questions about implementation details.
>
> • line 62: By convention, the optional 'save' function should be a key
> in the config dictionary. Only the simplest of functions use optional
> positional args in JavaScript, as getting around them is a pain
> (unlike in Python).
While I was doing this change I noticed that both Y.lp.picker.create and
Y.lp.picker.
(although it's not documented). I don't see a reason for that and all
their callsites pass in a config dictionary, as expected. Would it be
OK for me to change both methods to make the config required?
> • line 78: Might be better to use Y.Lang.isFunction()
Sure, I can do that.
> • line 116: Does YUI understand field IDs with dots in them yet? If
> it does, then the input_element string construction can be greatly
> simplified using a '#'
It doesn't yet, I tried. :/
> • line 163: What is the 'shadow' element? Some invisible element in
> the DOM? Just asking to make sure that 'shadow' is a behavioural
> object, not a presentation object :)
I *think* it's the shadow around the overlay; I've used it because it
was one of the few elements in the overlay that had an ID. But maybe I
didn't look carefully for other elements with an ID there.
> • line 170: Why do you have a hard sleep here?
Because that triggers a search, which might take a while to complete.
I think I read somewhere that the assertProperty() call will also wait
for the element to show up, in which case the sleep() would be
unnecessary. Is that why you asked?
> • Regarding the JSLint error: should we rename that file to .js.pt
> or .js.template, as it isn't a valid JavaScript file?
>
Yeah, .js.template sounds good to me.
--
Guilherme Salgado <email address hidden>
Māris Fogels (mars) wrote : | # |
On Tue, Jan 19, 2010 at 7:15 AM, Guilherme Salgado <email address hidden> wrote:
> On Mon, 2010-01-18 at 20:22 +0000, Māris Fogels wrote:
>> Review: Needs Fixing
>> Hi Salgado,
>>
>> Your changes look good, and thanks for updating lazr-js.
>
> Thanks for the review. In the future, would you mind including the diff
> in your review and adding your comments around the code you're referring
> to? That makes the conversation a lot smoother as we don't have to keep
> going back to the diff for context.
>
Ok. For JS reviews I find in-context diffs don't work, as I often spot problems in multiple places, with multiple problems per line. As a result, I note them like this:
line 23,45,67: foo is missing
line 23: bar should be baz
>>
>> I see one coding convention that needs fixing, and had some small
>> questions about implementation details.
>>
>> • line 62: By convention, the optional 'save' function should be a key
>> in the config dictionary. Only the simplest of functions use optional
>> positional args in JavaScript, as getting around them is a pain
>> (unlike in Python).
>
> While I was doing this change I noticed that both Y.lp.picker.create and
> Y.lp.picker.
> (although it's not documented). I don't see a reason for that and all
> their callsites pass in a config dictionary, as expected. Would it be
> OK for me to change both methods to make the config required?
Sometimes the config only contains optional settings, so you could obviously mark the entire config as an optional argument. However, for some objects, like the Y.Anim() object, the config contains required settings, and is *not* optional.
In this case, I leave the judgement up to you.
>> • line 163: What is the 'shadow' element? Some invisible element in
>> the DOM? Just asking to make sure that 'shadow' is a behavioural
>> object, not a presentation object :)
>
> I *think* it's the shadow around the overlay; I've used it because it
> was one of the few elements in the overlay that had an ID. But maybe I
> didn't look carefully for other elements with an ID there.
You might want to ask Tom how the bugs team tests for overlay presence and absence.
There are CSS classes associated with overlay state you may be able to use instead: yui-mywidgetnam
http://
>
>> • line 170: Why do you have a hard sleep here?
>
> Because that triggers a search, which might take a while to complete.
>
> I think I read somewhere that the assertProperty() call will also wait
> for the element to show up, in which case the sleep() would be
> unnecessary. Is that why you asked?
>
Nope, it is just habit to question a hard sleep like that. Usually you wait for an element in the DOM to show up to signal that the search is done.
Ok, this code looks good to me. Change what you desire, and r=mars.
Thanks,
Maris
Guilherme Salgado (salgado) wrote : | # |
On Tue, 2010-01-19 at 15:12 +0000, Māris Fogels wrote:
>
> On Tue, Jan 19, 2010 at 7:15 AM, Guilherme Salgado <email address hidden> wrote:
> > On Mon, 2010-01-18 at 20:22 +0000, Māris Fogels wrote:
> >> Review: Needs Fixing
> >> Hi Salgado,
> >>
> >> Your changes look good, and thanks for updating lazr-js.
> >
> > Thanks for the review. In the future, would you mind including the diff
> > in your review and adding your comments around the code you're referring
> > to? That makes the conversation a lot smoother as we don't have to keep
> > going back to the diff for context.
> >
>
> Ok. For JS reviews I find in-context diffs don't work, as I often
I don't think JS reviews are intrinsically different from, say, python
reviews.
> spot problems in multiple places, with multiple problems per line.
> As a result, I note them like this:
>
> line 23,45,67: foo is missing
> line 23: bar should be baz
When doing reviews I often spot problems that apply to multiple lines as
well, and in those cases I just copy-n-paste the comment or make a
reference to it. But it'd also be fine to have it at the top of the
message, I think, as long as the diff is included.
The fact that the diff is not included here means we have to switch back
and forth between the browser and the email client, which is a bit
disturbing.
>
> >>
> >> I see one coding convention that needs fixing, and had some small
> >> questions about implementation details.
> >>
> >> • line 62: By convention, the optional 'save' function should be a key
> >> in the config dictionary. Only the simplest of functions use optional
> >> positional args in JavaScript, as getting around them is a pain
> >> (unlike in Python).
> >
> > While I was doing this change I noticed that both Y.lp.picker.create and
> > Y.lp.picker.
> > (although it's not documented). I don't see a reason for that and all
> > their callsites pass in a config dictionary, as expected. Would it be
> > OK for me to change both methods to make the config required?
>
> Sometimes the config only contains optional settings, so you could obviously mark the entire config as an optional argument. However, for some objects, like the Y.Anim() object, the config contains required settings, and is *not* optional.
>
> In this case, I leave the judgement up to you.
I changed the docs to state that the config is optional.
>
>
> >> • line 163: What is the 'shadow' element? Some invisible element in
> >> the DOM? Just asking to make sure that 'shadow' is a behavioural
> >> object, not a presentation object :)
> >
> > I *think* it's the shadow around the overlay; I've used it because it
> > was one of the few elements in the overlay that had an ID. But maybe I
> > didn't look carefully for other elements with an ID there.
>
> You might want to ask Tom how the bugs team tests for overlay presence
> and absence.
>
> There are CSS classes associated with overlay state you may be able to
> use instead: yui-mywidgetnam
> yui-mywidget-
They use that on huge conditional xpaths, which look rather ugly and
hard to maintain -- that...
Māris Fogels (mars) wrote : | # |
Hi Salgado,
Your changes look good. The optional argument looks much cleaner. Thanks! r=mars
Maris
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2010-01-22 06:24:01 +0000 |
3 | +++ Makefile 2010-02-01 14:05:26 +0000 |
4 | @@ -357,9 +357,6 @@ |
5 | reload-apache: enable-apache-launchpad |
6 | /etc/init.d/apache2 restart |
7 | |
8 | -static: |
9 | - $(PY) scripts/make-static.py |
10 | - |
11 | TAGS: compile |
12 | # emacs tags |
13 | bin/tags -e |
14 | |
15 | === modified file 'lib/canonical/launchpad/javascript/bugs/bugtask-index.js' |
16 | --- lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2010-01-21 21:54:12 +0000 |
17 | +++ lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2010-02-01 14:05:26 +0000 |
18 | @@ -251,10 +251,10 @@ |
19 | picker_activator: '.menu-link-addsubscriber' |
20 | }; |
21 | |
22 | - var picker = Y.lp.picker.create( |
23 | - 'ValidPersonOrTeam', |
24 | - function(result) { subscribe_someone_else(result, subscription); }, |
25 | - config); |
26 | + config.save = function(result) { |
27 | + subscribe_someone_else(result, subscription); |
28 | + }; |
29 | + var picker = Y.lp.picker.create('ValidPersonOrTeam', config); |
30 | } |
31 | |
32 | /* |
33 | @@ -606,8 +606,8 @@ |
34 | picker_activator: '.menu-link-addbranch' |
35 | }; |
36 | |
37 | - var picker = Y.lp.picker.create( |
38 | - 'Branch', get_branch_and_link_to_bug, config); |
39 | + config.save = get_branch_and_link_to_bug; |
40 | + var picker = Y.lp.picker.create('Branch', config); |
41 | } |
42 | } |
43 | |
44 | |
45 | === modified file 'lib/canonical/launchpad/javascript/code/codereview.js' |
46 | --- lib/canonical/launchpad/javascript/code/codereview.js 2010-01-15 04:34:57 +0000 |
47 | +++ lib/canonical/launchpad/javascript/code/codereview.js 2010-02-01 14:05:26 +0000 |
48 | @@ -139,13 +139,11 @@ |
49 | step_title: 'Search' |
50 | }; |
51 | |
52 | - reviewer_picker = Y.lp.picker.create( |
53 | - 'ValidPersonOrTeam', |
54 | - function(result) { |
55 | - var review_type = Y.one("[id=field.review_type]").get('value'); |
56 | - request_reviewer(result, review_type); |
57 | - }, |
58 | - config); |
59 | + config.save = function(result) { |
60 | + var review_type = Y.one("[id=field.review_type]").get('value'); |
61 | + request_reviewer(result, review_type); |
62 | + }; |
63 | + reviewer_picker = Y.lp.picker.create('ValidPersonOrTeam', config); |
64 | reviewer_picker.set('footer_slot', Y.Node.create([ |
65 | '<div>', |
66 | '<div style="float: left; padding-right: 9px;">', |
67 | |
68 | === modified file 'lib/canonical/launchpad/javascript/lp/picker.js' |
69 | --- lib/canonical/launchpad/javascript/lp/picker.js 2009-12-09 15:07:39 +0000 |
70 | +++ lib/canonical/launchpad/javascript/lp/picker.js 2010-02-01 14:05:26 +0000 |
71 | @@ -150,7 +150,8 @@ |
72 | }); |
73 | }; |
74 | |
75 | - var picker = Y.lp.picker.create(vocabulary, save, config); |
76 | + config.save = save; |
77 | + var picker = Y.lp.picker.create(vocabulary, config); |
78 | picker._resource_uri = resource_uri; |
79 | var extra_buttons = Y.Node.create( |
80 | '<div style="text-align: center; height: 3em; ' + |
81 | @@ -192,17 +193,18 @@ |
82 | * @requires dom, dump, lazr.overlay, lazr.picker |
83 | * @method create |
84 | * @param {String} vocabulary Name of the vocabulary to query. |
85 | - * @param {Function} save Function which takes a single string argument. |
86 | - * @param {Object} config Object literal of config name/value pairs. |
87 | + * @param {Object} config Optional Object literal of config name/value pairs. |
88 | * config.header is a line of text at the top of |
89 | * the widget. |
90 | * config.step_title overrides the subtitle. |
91 | - * description strings. |
92 | + * config.save is a Function (optional) which takes |
93 | + * a single string argument. |
94 | */ |
95 | -Y.lp.picker.create = function (vocabulary, save, config) { |
96 | +Y.lp.picker.create = function (vocabulary, config) { |
97 | if (Y.UA.ie) { |
98 | return; |
99 | } |
100 | + |
101 | if (config !== undefined) { |
102 | var header = 'Choose an item.'; |
103 | if (config.header !== undefined) { |
104 | @@ -233,13 +235,13 @@ |
105 | zIndex: 1000, |
106 | visible: false |
107 | }); |
108 | - var picker = new Y.Picker(new_config); |
109 | + var picker = new Y.lazr.Picker(new_config); |
110 | |
111 | picker.subscribe('save', function (e) { |
112 | Y.log('Got save event.'); |
113 | - // Y.one() uses CSS3 selectors which don't work with ids containing |
114 | - // a period, so we have to use Y.DOM.byId(). |
115 | - save(e.details[Y.Picker.SAVE_RESULT]); |
116 | + if (Y.Lang.isFunction(config.save)) { |
117 | + config.save(e.details[Y.lazr.Picker.SAVE_RESULT]); |
118 | + } |
119 | }); |
120 | |
121 | picker.subscribe('cancel', function (e) { |
122 | |
123 | === modified file 'lib/canonical/launchpad/javascript/registry/team.js' |
124 | --- lib/canonical/launchpad/javascript/registry/team.js 2009-12-22 20:30:39 +0000 |
125 | +++ lib/canonical/launchpad/javascript/registry/team.js 2010-02-01 14:05:26 +0000 |
126 | @@ -25,7 +25,8 @@ |
127 | picker_activator: '.menu-link-add_member' |
128 | }; |
129 | |
130 | - Y.lp.picker.create('ValidTeamMember', _add_member, config); |
131 | + config.save = _add_member; |
132 | + Y.lp.picker.create('ValidTeamMember', config); |
133 | }; |
134 | |
135 | var _add_member = function(selected_person) { |
136 | |
137 | === modified file 'lib/canonical/launchpad/windmill/testing/lpuser.py' |
138 | --- lib/canonical/launchpad/windmill/testing/lpuser.py 2009-10-27 13:01:29 +0000 |
139 | +++ lib/canonical/launchpad/windmill/testing/lpuser.py 2010-02-01 14:05:26 +0000 |
140 | @@ -19,6 +19,8 @@ |
141 | |
142 | def ensure_login(self, client): |
143 | """Ensure that this user is logged on the page under windmill.""" |
144 | + # Refresh the page to make sure we're not fooled by a stale one. |
145 | + client.refresh() |
146 | client.waits.forPageLoad(timeout=constants.PAGE_LOAD) |
147 | result = client.asserts.assertNode( |
148 | name=u'loginpage_submit_login', assertion=False) |
149 | |
150 | === modified file 'lib/canonical/widgets/popup.py' |
151 | --- lib/canonical/widgets/popup.py 2009-07-30 17:58:16 +0000 |
152 | +++ lib/canonical/widgets/popup.py 2010-02-01 14:05:26 +0000 |
153 | @@ -127,7 +127,7 @@ |
154 | |
155 | def chooseLink(self): |
156 | js_file = os.path.join(os.path.dirname(__file__), |
157 | - 'templates/vocabulary-picker.js') |
158 | + 'templates/vocabulary-picker.js.template') |
159 | js_template = open(js_file).read() |
160 | |
161 | if self.header is None: |
162 | |
163 | === renamed file 'lib/canonical/widgets/templates/vocabulary-picker.js' => 'lib/canonical/widgets/templates/vocabulary-picker.js.template' |
164 | --- lib/canonical/widgets/templates/vocabulary-picker.js 2009-09-21 12:27:14 +0000 |
165 | +++ lib/canonical/widgets/templates/vocabulary-picker.js.template 2010-02-01 14:05:26 +0000 |
166 | @@ -1,4 +1,4 @@ |
167 | -YUI().use('node', 'lp.picker', function(Y) { |
168 | +YUI().use('node', 'lp.picker', 'plugin', function(Y) { |
169 | if (Y.UA.ie) { |
170 | return; |
171 | } |
172 | @@ -9,15 +9,12 @@ |
173 | // The vocabulary picker, created when used for the first time. |
174 | var picker = null; |
175 | function make_picker() { |
176 | - var save = function (result) { |
177 | - Y.DOM.byId(args.input_id).value = result.value; |
178 | - }; |
179 | var config = { |
180 | header: args.header, |
181 | step_title: args.step_title, |
182 | extra_no_results_message: args.extra_no_results_message |
183 | }; |
184 | - var picker = Y.lp.picker.create(args.vocabulary, save, config); |
185 | + var picker = Y.lp.picker.create(args.vocabulary, config); |
186 | if (config.extra_no_results_message !== null) { |
187 | picker.before('resultsChange', function (e) { |
188 | var new_results = e.details[0].newVal; |
189 | @@ -30,6 +27,8 @@ |
190 | } |
191 | }); |
192 | } |
193 | + picker.plug(Y.lazr.TextFieldPickerPlugin, |
194 | + {input_element: '[id="' + args.input_id + '"]'}); |
195 | return picker; |
196 | } |
197 | |
198 | |
199 | === added file 'lib/lp/registry/windmill/tests/test_person_picker.py' |
200 | --- lib/lp/registry/windmill/tests/test_person_picker.py 1970-01-01 00:00:00 +0000 |
201 | +++ lib/lp/registry/windmill/tests/test_person_picker.py 2010-02-01 14:05:26 +0000 |
202 | @@ -0,0 +1,55 @@ |
203 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
204 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
205 | + |
206 | +"""Test for using a person picker widget.""" |
207 | + |
208 | +__metaclass__ = type |
209 | +__all__ = [] |
210 | + |
211 | +import unittest |
212 | + |
213 | +from windmill.authoring import WindmillTestClient |
214 | + |
215 | +from canonical.launchpad.windmill.testing import constants, lpuser |
216 | + |
217 | +from lp.registry.windmill.testing import RegistryWindmillLayer |
218 | +from lp.testing import TestCaseWithFactory |
219 | + |
220 | + |
221 | +class TesPersonPickerWidget(TestCaseWithFactory): |
222 | + |
223 | + layer = RegistryWindmillLayer |
224 | + |
225 | + def setUp(self): |
226 | + super(TesPersonPickerWidget, self).setUp() |
227 | + self.client = WindmillTestClient('PersonPickerWidget') |
228 | + |
229 | + def test_person_picker_widget(self): |
230 | + client = self.client |
231 | + lpuser.SAMPLE_PERSON.ensure_login(client) |
232 | + |
233 | + client.open(url=u'http://launchpad.dev:8085/people/+requestmerge') |
234 | + client.waits.forPageLoad(timeout=constants.PAGE_LOAD) |
235 | + client.waits.forElement(id=u'show-widget-field-dupe_person', |
236 | + timeout=constants.FOR_ELEMENT) |
237 | + |
238 | + client.type(text=u'guilherme', name=u'field.dupe_person') |
239 | + |
240 | + client.click(id=u'show-widget-field-dupe_person') |
241 | + client.waits.forElement(id=u'shadow', timeout=constants.FOR_ELEMENT) |
242 | + |
243 | + client.asserts.assertProperty( |
244 | + xpath=u'//div[@class="yui-picker-search-box"]/input', |
245 | + validator=u'value|guilherme') |
246 | + |
247 | + client.click(xpath=u'//div[@class="yui-picker-search-box"]/button') |
248 | + client.waits.sleep(milliseconds=constants.SLEEP) |
249 | + |
250 | + client.click(xpath=u'//ul[@class="yui-picker-results"]/li[1]') |
251 | + client.asserts.assertProperty( |
252 | + xpath=u'//input[@name="field.dupe_person"]', |
253 | + validator='value|salgado') |
254 | + |
255 | + |
256 | +def test_suite(): |
257 | + return unittest.TestLoader().loadTestsFromName(__name__) |
258 | |
259 | === modified file 'versions.cfg' |
260 | --- versions.cfg 2010-01-22 04:01:17 +0000 |
261 | +++ versions.cfg 2010-02-01 14:05:26 +0000 |
262 | @@ -37,7 +37,7 @@ |
263 | lazr.smtptest = 1.1 |
264 | lazr.testing = 0.1.1 |
265 | lazr.uri = 1.0.2 |
266 | -lazr-js = 0.9.2DEV |
267 | +lazr-js = 0.9.2 |
268 | martian = 0.11 |
269 | mechanize = 0.1.11 |
270 | mercurial = 1.3.1 |
= Summary =
Change our make_picker() function to use the new TextFieldPicker Plugin plugin so that the picker's default value
Y.lazr.
is taken from the input field associated with it.
Also updates to a newer lazr-js, containing just one extra revision with
the fix for bug 482235.
== Tests ==
./bin/test test_person_picker
== Demo and Q/A ==
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /widgets/ templates/ vocabulary- picker. js /launchpad/ javascript/ lp/picker. js /launchpad/ javascript/ bugs/bugtask- index.js /launchpad/ javascript/ code/codereview .js registry/ windmill/ tests/test_ person_ picker. py
versions.cfg
lib/canonical
lib/canonical
lib/canonical
lib/canonical
lib/lp/
== JSLint notices == salgado/ devel/launchpad /bug-482235/lib/canonical/ launchpad/ javascript/ bugs/bugtask- index.js' .
jslint: No problem found in '/home/
jslint: No problem found in '/home/ salgado/ devel/launchpad /bug-482235/lib/canonical/ launchpad/ javascript/ code/codereview .js'.
jslint: No problem found in '/home/ salgado/ devel/launchpad /bug-482235/lib/canonical/ launchpad/ javascript/ lp/picker. js'.
jslint: Lint found in '/home/ salgado/ devel/launchpad /bug-482235/lib/canonical/ widgets/ templates/ vocabulary- picker. js':
Line 8 character 16: Expected an identifier and instead saw '%'.
var args = %s;
Line 8 character 16: Stopping, unable to continue. (14% scanned).
The lint error above is expected because that file is actually a
template used from within python code.