Merge lp:~jcsackett/launchpad/oh-oh-pick-me-pick-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: 13149
Proposed branch: lp:~jcsackett/launchpad/oh-oh-pick-me-pick-me
Merge into: lp:launchpad
Diff against target: 215 lines (+181/-2)
4 files modified
lib/lp/app/widgets/popup.py (+1/-2)
lib/lp/registry/javascript/personpicker.js (+74/-0)
lib/lp/registry/javascript/tests/test_personpicker.html (+40/-0)
lib/lp/registry/javascript/tests/test_personpicker.js (+66/-0)
To merge this branch: bzr merge lp:~jcsackett/launchpad/oh-oh-pick-me-pick-me
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code Approve
Review via email: mp+63047@code.launchpad.net

Commit message

Creates a personpicker YUI widget

Description of the change

Summary
=======
As part of the work for disclosure, a better personpicker is needed that users can trust. The current personpicker is actually just the regular picker, passed a person-specific vocabulary. Because the personpicker will have it's own UI attributes (e.g. "Assign me" and "Remove assignee" buttons like the bug assign pickerPatcher), we need a PersonPicker YUI widget.

Preimplementation
=================
Spoke with Deryck Hodge about creating a new widget, and Curtis Hovey.

Implementation
==============
lib/lp/app/widgets/popup.py
---------------------------
Drive by whitespace fixes.

lib/lp/registry/javascript/personpicker.js
------------------------------------------
Created PersonPicker, which extends Y.lazr.Picker. It creates an assign_me and a remove_asignee button, as those are UI elements we want when using the person picker, and wires them up to provide the users name or to clear the entry as appropriate.

lib/lp/registry/javascript/tests/test_personpicker.html
lib/lp/registry/javascript/tests/test_personpicker.js
-----------------------------------------------------
Tests.

Tests
=====
firefox lib/lp/registry/javascript/tests/test_personpicker.html

QA
==
None. This is a new widget not yet wired into any of the UI.

Lint
====
= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/widgets/popup.py
  lib/lp/registry/javascript/personpicker.js
  lib/lp/registry/javascript/tests/test_personpicker.html
  lib/lp/registry/javascript/tests/test_personpicker.js

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

Could you attach a screenshot please?

Revision history for this message
Steve Kowalik (stevenk) wrote :

This looks good to me, but I share Martin's concerns, that:

1. This will require a UI review.
2. I'd like to see how this looks and behaves before I approve it.

review: Needs Information (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :

> This looks good to me, but I share Martin's concerns, that:
>
> 1. This will require a UI review.
> 2. I'd like to see how this looks and behaves before I approve it.

Screenshot is available here: http://people.canonical.com/~jc/images/disclosure/personpicker.png

Revision history for this message
Steve Kowalik (stevenk) :
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/app/widgets/popup.py'
2--- lib/lp/app/widgets/popup.py 2011-05-29 23:34:26 +0000
3+++ lib/lp/app/widgets/popup.py 2011-06-01 14:02:31 +0000
4@@ -8,8 +8,6 @@
5 __metaclass__ = type
6
7 import cgi
8-
9-import re
10 import simplejson
11 from z3c.ptcompat import ViewPageTemplateFile
12 from zope.app.form.browser.itemswidgets import (
13@@ -156,6 +154,7 @@
14
15
16 class PersonPickerWidget(VocabularyPickerWidget):
17+
18 include_create_team_link = False
19
20 def chooseLink(self):
21
22=== added file 'lib/lp/registry/javascript/personpicker.js'
23--- lib/lp/registry/javascript/personpicker.js 1970-01-01 00:00:00 +0000
24+++ lib/lp/registry/javascript/personpicker.js 2011-06-01 14:02:31 +0000
25@@ -0,0 +1,74 @@
26+/* Copyright 2011 Canonical Ltd. This software is licensed under the
27+ * GNU Affero General Public License version 3 (see the file LICENSE).
28+ *
29+ * @namespace Y.lp.registry.personpicker
30+ * @requires lazr.picker
31+ */
32+YUI.add('lp.registry.personpicker', function(Y) {
33+var namespace = Y.namespace('lp.registry.personpicker');
34+
35+var footer_label = ".yui3-picker-footer-slot"
36+
37+var PersonPicker = function() {
38+ PersonPicker.superclass.constructor.apply(this, arguments);
39+
40+ this._extra_buttons = Y.Node.create('<div class="extra-form-buttons"/>');
41+};
42+
43+Y.extend(PersonPicker, Y.lazr.Picker, {
44+ hide: function() {
45+ this.get('boundingBox').setStyle('display', 'none');
46+ },
47+
48+ show: function() {
49+ this.get('boundingBox').setStyle('display', 'block');
50+ },
51+
52+ remove: function () {
53+ this.fire('save', {value: ''});
54+ },
55+
56+ assign_me: function () {
57+ name = LP.links.me.replace('/~', '');
58+ this.fire('save', {value: name});
59+ },
60+
61+ renderUI: function() {
62+ this.constructor.superclass.renderUI.call(this);
63+ var remove_button, assign_me_button;
64+ //# TODO config should set extrabuttons
65+ var show_remove_button = true;
66+ var show_assign_me_button = true;
67+ var remove_button_text = "Remove assignee";
68+ var assign_me_button_text = "Assign me";
69+ if (show_remove_button) {
70+ remove_button = Y.Node.create(
71+ '<a class="yui-picker-remove-button bg-image" ' +
72+ 'href="javascript:void(0)" ' +
73+ 'style="background-image: url(/@@/remove); padding-right: 1em">' +
74+ remove_button_text + '</a>');
75+ remove_button.on('click', this.remove, this);
76+ this._extra_buttons.appendChild(remove_button);
77+ }
78+ if (show_assign_me_button) {
79+ assign_me_button = Y.Node.create(
80+ '<a class="yui-picker-assign-me-button bg-image" ' +
81+ 'href="javascript:void(0)" ' +
82+ 'style="background-image: url(/@@/person)">' +
83+ assign_me_button_text + '</a>');
84+ assign_me_button.on('click', this.assign_me, this);
85+ this._extra_buttons.appendChild(assign_me_button);
86+ }
87+ },
88+
89+ syncUI: function() {
90+ // call Picker's sync
91+ this.constructor.superclass.syncUI.call(this);
92+ footer_slot = Y.one(footer_label);
93+ footer_slot.appendChild(this._extra_buttons);
94+ }
95+});
96+PersonPicker.NAME = 'person-picker';
97+namespace.PersonPicker = PersonPicker
98+
99+}, "0.1", {"requires": ['lazr.picker']});
100
101=== added file 'lib/lp/registry/javascript/tests/test_personpicker.html'
102--- lib/lp/registry/javascript/tests/test_personpicker.html 1970-01-01 00:00:00 +0000
103+++ lib/lp/registry/javascript/tests/test_personpicker.html 2011-06-01 14:02:31 +0000
104@@ -0,0 +1,40 @@
105+<html>
106+ <head>
107+ <title>Launchpad PersonPicker</title>
108+ <!-- YUI 3.0 Setup -->
109+ <script type="text/javascript" src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
110+ <script type="text/javascript"
111+ src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script>
112+ <link rel="stylesheet"
113+ href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/>
114+ <link rel="stylesheet"
115+ href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/>
116+ <link rel="stylesheet"
117+ href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/>
118+ <link rel="stylesheet"
119+ href="../../../../canonical/launchpad/javascript/test.css" />
120+ <script type="text/javascript" src="../../../app/javascript/client.js"></script>
121+ <script type="text/javascript" src="../../../app/javascript/lp.js"></script>
122+
123+ <!-- The module under test -->
124+ <script type="text/javascript" src="../personpicker.js"></script>
125+
126+ <!-- The test suite -->
127+ <script type="text/javascript" src="test_personpicker.js"></script>
128+ </head>
129+ <body class="yui-skin-sam">
130+
131+ <!-- The example markup required by the script to run -->
132+ <input type="text" value="" id="choose"
133+ name="choose" size="20"
134+ maxlength=""
135+ onKeyPress="" style=""
136+ class="" />
137+ <span class="">
138+ (<a id="show-widget" href="/people/">Find&hellip;</a>)
139+ </span>
140+
141+ <!-- The test output -->
142+ <div id="log"></div>
143+ </body>
144+</html>
145
146=== added file 'lib/lp/registry/javascript/tests/test_personpicker.js'
147--- lib/lp/registry/javascript/tests/test_personpicker.js 1970-01-01 00:00:00 +0000
148+++ lib/lp/registry/javascript/tests/test_personpicker.js 2011-06-01 14:02:31 +0000
149@@ -0,0 +1,66 @@
150+/* Copyright 2011 Canonical Ltd. This software is licensed under the
151+ * GNU Affero General Public License version 3 (see the file LICENSE).
152+ */
153+
154+YUI({
155+ base: '../../../../canonical/launchpad/icing/yui/',
156+ filter: 'raw', combine: false,
157+ fetchCSS: false
158+ }).use('test', 'console', 'plugin', 'lp.registry.personpicker',
159+ 'node-event-simulate', function(Y) {
160+
161+ var suite = new Y.Test.Suite("lp.registry.personpicker Tests");
162+
163+ suite.add(new Y.Test.Case({
164+ name: 'personpicker',
165+
166+ setUp: function() {
167+ window.LP = {
168+ links: {me: '/~no-one'},
169+ cache: {}
170+ }
171+ },
172+
173+ test_render: function () {
174+ var personpicker = new Y.lp.registry.personpicker.PersonPicker();
175+ personpicker.render();
176+ personpicker.show();
177+
178+ // The extra buttons section exists
179+ Y.Assert.isNotNull(Y.one('.extra-form-buttons'));
180+ Y.Assert.isNotNull(Y.one('.yui-picker-remove-button'));
181+ Y.Assert.isNotNull(Y.one('.yui-picker-assign-me-button'));
182+ },
183+
184+ test_buttons: function () {
185+ var personpicker = new Y.lp.registry.personpicker.PersonPicker();
186+ personpicker.render();
187+ personpicker.show();
188+
189+ // Patch the picker so the assign_me and remove methods can be
190+ // tested.
191+ var data = null;
192+ personpicker.on('save', function (result) {
193+ data = result.value;
194+ });
195+ var remove = Y.one('.yui-picker-remove-button');
196+ remove.simulate('click');
197+ Y.Assert.areEqual('', data);
198+
199+ var assign_me = Y.one('.yui-picker-assign-me-button');
200+ assign_me.simulate('click');
201+ Y.Assert.areEqual('no-one', data);
202+ }
203+ }));
204+
205+ // Lock, stock, and two smoking barrels.
206+ Y.Test.Runner.add(suite);
207+
208+ var console = new Y.Console({newestOnTop: false});
209+ console.render('#log');
210+
211+ Y.on('domready', function() {
212+ Y.Test.Runner.run();
213+ });
214+});
215+