Merge lp:~salgado/lazr-js/bug-482235 into lp:lazr-js

Proposed by Guilherme Salgado
Status: Superseded
Proposed branch: lp:~salgado/lazr-js/bug-482235
Merge into: lp:lazr-js
Diff against target: 97 lines (+44/-0)
3 files modified
examples/picker/index.html (+10/-0)
src-js/lazrjs/picker/picker.js (+25/-0)
src-js/lazrjs/picker/tests/picker.js (+9/-0)
To merge this branch: bzr merge lp:~salgado/lazr-js/bug-482235
Reviewer Review Type Date Requested Status
Māris Fogels (community) Abstain
Björn Tillenius (community) Abstain
Review via email: mp+15940@code.launchpad.net

This proposal has been superseded by a proposal from 2009-12-18.

To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :

Add a new attribute (get_initial_value_from) to the picker. When that attribute is set, the initial value for the picker's search input will come from the page element identified by it

Revision history for this message
Björn Tillenius (bjornt) wrote :

The feature itself looks very useful. I have some comments and questions, though. First of all, reading the diff, it looks like it only sets the search text, but doesn't actually search. But I assume that it does perform an actual search, no?

I'm also not so sure about the API. I can see how you chose to add a parameter, since that fits into the current API. However, I think it's worth thinking about, whether we can do something better. The reason I'm a bit skeptic to having yet another parameter, is that you mostly want to use this when you have a text input field, right? And when you have a text input field, you mostly always want this functionality, and you mostly always want to put pack the search result from the picker into the text field, right? With the current API, that means that every time you connect the picker to a text field, you have to do two things, specifying the same text field twice.

I want us to specify the text field only once. A possible solution would be to use the plugin API to achieve this. So that you have something like:

    var picker = new Y.Picker();
    picker.plug(TextFieldPickerPlugin('#text-field'));

Compare that to the existing API which looks something like this:

    var picker = new Y.Picker({get_initial_value_from: '#text-field'});
    picker.subscribe('save', function (e) {
        Y.DOM.byId('#text-field').value = e.details[Y.Picker.SAVE_RESULT].value;
    });

review: Abstain
Revision history for this message
Māris Fogels (mars) wrote :

I agree, and as I stated in the original bug report, the API could be improved.

I would not mind building the initial_value attribute as described in the bug report into the lazr-js class, but I think Björn's suggestion works too.

Björn's idea can be simplified a bit. Assuming we want the functionality across all of Launchpad, you can plug into the Picker class itself using the class-level static .plug() method.

Instead of this:

>
> var picker = new Y.Picker();
> picker.plug(TextFieldPickerPlugin('#text-field'));
>

You do this:

Y.Picker.plug(TextFieldPickerPlugin);
var picker = Y.Picker({
  ...
  use_value_from: '#foobar' // Our new attribute
});

The class-level plugin could then add the attributes it needs dynamically.

review: Abstain
Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (3.8 KiB)

> The feature itself looks very useful. I have some comments and questions,
> though. First of all, reading the diff, it looks like it only sets the search
> text, but doesn't actually search. But I assume that it does perform an actual
> search, no?

Currently, it doesn't but that's trivial to add.

>
> I'm also not so sure about the API. I can see how you chose to add a
> parameter, since that fits into the current API. However, I think it's worth
> thinking about, whether we can do something better. The reason I'm a bit
> skeptic to having yet another parameter, is that you mostly want to use this
> when you have a text input field, right? And when you have a text input field,
> you mostly always want this functionality, and you mostly always want to put
> pack the search result from the picker into the text field, right? With the
> current API, that means that every time you connect the picker to a text
> field, you have to do two things, specifying the same text field twice.
>
> I want us to specify the text field only once. A possible solution would be to
> use the plugin API to achieve this. So that you have something like:
>
> var picker = new Y.Picker();
> picker.plug(TextFieldPickerPlugin('#text-field'));

Below is what I came up with, but notice the ID of the text field is not passed to TexFieldPickerPlugin (I didn't find out how to do that).

One thing that's not clear to me is the advantage of using the plugin API instead of just subclassing the picker and adding the functionality to the subclass? AFAICT, subclassing would work just fine and it would be certainly simpler.

=== modified file 'lib/canonical/widgets/templates/vocabulary-picker.js'
--- lib/canonical/widgets/templates/vocabulary-picker.js 2009-09-21 12:27:14 +0000
+++ lib/canonical/widgets/templates/vocabulary-picker.js 2009-12-11 19:54:11 +0000
@@ -1,4 +1,4 @@
-YUI().use('node', 'lp.picker', function(Y) {
+YUI().use('node', 'lp.picker', 'plugin', function(Y) {
     if (Y.UA.ie) {
         return;
     }
@@ -6,18 +6,39 @@
     // Args from python.
     var args = %s;

+ function TextFieldPickerPlugin(config) {
+ TextFieldPickerPlugin.superclass.constructor.apply(this, arguments);
+ }
+
+ TextFieldPickerPlugin.NAME = 'TextFieldPickerPlugin';
+ TextFieldPickerPlugin.NS = 'txtpicker';
+
+ Y.extend(TextFieldPickerPlugin, Y.Plugin.Base, {
+ initializer: function(config) {
+ // Y.one() uses CSS3 selectors which don't work with ids
+ // containing a period, so we have to use Y.DOM.byId().
+ var input = Y.DOM.byId(args.input_id);
+ this.doAfter('save', function (e) {
+ result = e.details[Y.Picker.SAVE_RESULT];
+ input.value = result.value;
+ });
+ this.doAfter('show', function() {
+ if ( input.value ) {
+ this.get('host')._search_input.set('value', input.value);
+ }
+ });
+ }
+ });
+
     // The vocabulary picker, created when used for the first time.
     var picker = null;
     function make_picker() {
- var save = function (result) {
- Y.DOM.byId(args...

Read more...

Revision history for this message
Māris Fogels (mars) wrote :

On Mon, Dec 14, 2009 at 2:32 PM, Guilherme Salgado
<email address hidden> wrote:
>
> One thing that's not clear to me is the advantage of using the plugin API instead of just subclassing the picker and adding the functionality to the subclass?  AFAICT, subclassing would work just fine and it would be certainly simpler.
>

YUI widgets were not designed to be subclassed for the reason are thinking.

Subclassing a YUI widget means literally a new widget built on top of
the old functionality. You must give the widget a new .NAME
attribute, new CSS, a new renderUI, bindUI, and syncUI methods, etc.

YUI leans towards composing widgets to add features, rather than
subclassing to add them. That is what the Plugin API and
Y.Base.build() are for. Thus, that is why I suggested using the
Plugin API or the Extension API. In this case, a Plugin is simplest.

lp:~salgado/lazr-js/bug-482235 updated
165. By Guilherme Salgado

merge from trunk

166. By Guilherme Salgado

Reimplement the thing using a plugin

167. By Guilherme Salgado

Move Picker and TextFieldPickerPlugin under tha lazr (Y.lazr) namespace

168. By Guilherme Salgado

Docs and minor changes suggested by Bjorn

169. By Guilherme Salgado

Add an example showing how to use Y.lazr.TextFieldPickerPlugin

170. By Guilherme Salgado

A couple minor tweaks suggested by Maris

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'examples/picker/index.html'
--- examples/picker/index.html 2009-11-30 19:07:28 +0000
+++ examples/picker/index.html 2009-12-10 12:49:12 +0000
@@ -72,6 +72,7 @@
7272
7373
74 var picker = new Y.Picker({74 var picker = new Y.Picker({
75 get_initial_value_from: '#initval',
75 picker_activator: '#show-widget',76 picker_activator: '#show-widget',
76 clear_on_cancel: true,77 clear_on_cancel: true,
77 align: {78 align: {
@@ -225,6 +226,10 @@
225 </textarea>226 </textarea>
226 </div>227 </div>
227 <div>228 <div>
229 <b>Initial value:</b>
230 <input type="text" id="initval" />
231 </div>
232 <div>
228 <b>Last item selected:</b> <span id="selected-item">None</span>233 <b>Last item selected:</b> <span id="selected-item">None</span>
229 </div>234 </div>
230 <div>235 <div>
@@ -295,6 +300,11 @@
295 are no search results, the <code>search_slot</code>300 are no search results, the <code>search_slot</code>
296 and <code>footer_slot</code> are displayed next to each other.301 and <code>footer_slot</code> are displayed next to each other.
297 </li>302 </li>
303 <li><code>get_initial_value_from</code> (default <code>null</code>) -
304 When set this should be the CSS selector for the page element that
305 contains the initial value for the picker's search input. The page
306 element must have a 'value' attribute.
307 </li>
298 </ul>308 </ul>
299 <p>Here's some sample code that creates a Picker. Note that it's usually a309 <p>Here's some sample code that creates a Picker. Note that it's usually a
300 good idea for you to create one Picker, showing and hiding it as needed,310 good idea for you to create one Picker, showing and hiding it as needed,
301311
=== modified file 'src-js/lazrjs/picker/picker.js'
--- src-js/lazrjs/picker/picker.js 2009-11-24 00:09:41 +0000
+++ src-js/lazrjs/picker/picker.js 2009-12-10 12:49:12 +0000
@@ -184,6 +184,22 @@
184 },184 },
185185
186 /**186 /**
187 * Override Y.Widget.show() so that we can set the initial value of the
188 * search input.
189 *
190 * @method show
191 */
192 show: function() {
193 if (this.get('get_initial_value_from')) {
194 var value = Y.one(this.get('get_initial_value_from')).get('value');
195 if ( value ) {
196 this._search_input.set('value', value);
197 }
198 }
199 Picker.superclass.show.apply(this);
200 },
201
202 /**
187 * Update the container for extra form inputs.203 * Update the container for extra form inputs.
188 *204 *
189 * @method _syncSearchSlotUI205 * @method _syncSearchSlotUI
@@ -698,6 +714,15 @@
698714
699Picker.ATTRS = {715Picker.ATTRS = {
700 /**716 /**
717 * The CSS selector for the page element from where the initial value of
718 * the picker's search input is taken.
719 *
720 * @attribute get_initial_value_from
721 * @type String
722 */
723 get_initial_value_from: { value: null },
724
725 /**
701 * Whether or not the search box and result list should be cleared when726 * Whether or not the search box and result list should be cleared when
702 * the save event is fired.727 * the save event is fired.
703 *728 *
704729
=== modified file 'src-js/lazrjs/picker/tests/picker.js'
--- src-js/lazrjs/picker/tests/picker.js 2009-11-30 19:07:28 +0000
+++ src-js/lazrjs/picker/tests/picker.js 2009-12-10 12:49:12 +0000
@@ -82,6 +82,15 @@
82 bb.query('.yui-picker-error'), "Missing error box.");82 bb.query('.yui-picker-error'), "Missing error box.");
83 },83 },
8484
85 test_initial_value: function () {
86 var picker = new Y.Picker({get_initial_value_from: '#initval'});
87 var node = Y.one(document.body).appendChild(
88 Y.Node.create('<input id="initval" value="foo" />'));
89 picker.render();
90 picker.show();
91 Assert.isTrue(picker._search_input.get('value') == 'foo');
92 },
93
85 test_set_results_updates_display: function () {94 test_set_results_updates_display: function () {
86 this.picker.render();95 this.picker.render();
87 var image_url = '../../lazr/assets/skins/sam/search.png';96 var image_url = '../../lazr/assets/skins/sam/search.png';

Subscribers

People subscribed via source and target branches