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
1=== modified file 'examples/picker/index.html'
2--- examples/picker/index.html 2009-11-30 19:07:28 +0000
3+++ examples/picker/index.html 2009-12-10 12:49:12 +0000
4@@ -72,6 +72,7 @@
5
6
7 var picker = new Y.Picker({
8+ get_initial_value_from: '#initval',
9 picker_activator: '#show-widget',
10 clear_on_cancel: true,
11 align: {
12@@ -225,6 +226,10 @@
13 </textarea>
14 </div>
15 <div>
16+ <b>Initial value:</b>
17+ <input type="text" id="initval" />
18+ </div>
19+ <div>
20 <b>Last item selected:</b> <span id="selected-item">None</span>
21 </div>
22 <div>
23@@ -295,6 +300,11 @@
24 are no search results, the <code>search_slot</code>
25 and <code>footer_slot</code> are displayed next to each other.
26 </li>
27+ <li><code>get_initial_value_from</code> (default <code>null</code>) -
28+ When set this should be the CSS selector for the page element that
29+ contains the initial value for the picker's search input. The page
30+ element must have a 'value' attribute.
31+ </li>
32 </ul>
33 <p>Here's some sample code that creates a Picker. Note that it's usually a
34 good idea for you to create one Picker, showing and hiding it as needed,
35
36=== modified file 'src-js/lazrjs/picker/picker.js'
37--- src-js/lazrjs/picker/picker.js 2009-11-24 00:09:41 +0000
38+++ src-js/lazrjs/picker/picker.js 2009-12-10 12:49:12 +0000
39@@ -184,6 +184,22 @@
40 },
41
42 /**
43+ * Override Y.Widget.show() so that we can set the initial value of the
44+ * search input.
45+ *
46+ * @method show
47+ */
48+ show: function() {
49+ if (this.get('get_initial_value_from')) {
50+ var value = Y.one(this.get('get_initial_value_from')).get('value');
51+ if ( value ) {
52+ this._search_input.set('value', value);
53+ }
54+ }
55+ Picker.superclass.show.apply(this);
56+ },
57+
58+ /**
59 * Update the container for extra form inputs.
60 *
61 * @method _syncSearchSlotUI
62@@ -698,6 +714,15 @@
63
64 Picker.ATTRS = {
65 /**
66+ * The CSS selector for the page element from where the initial value of
67+ * the picker's search input is taken.
68+ *
69+ * @attribute get_initial_value_from
70+ * @type String
71+ */
72+ get_initial_value_from: { value: null },
73+
74+ /**
75 * Whether or not the search box and result list should be cleared when
76 * the save event is fired.
77 *
78
79=== modified file 'src-js/lazrjs/picker/tests/picker.js'
80--- src-js/lazrjs/picker/tests/picker.js 2009-11-30 19:07:28 +0000
81+++ src-js/lazrjs/picker/tests/picker.js 2009-12-10 12:49:12 +0000
82@@ -82,6 +82,15 @@
83 bb.query('.yui-picker-error'), "Missing error box.");
84 },
85
86+ test_initial_value: function () {
87+ var picker = new Y.Picker({get_initial_value_from: '#initval'});
88+ var node = Y.one(document.body).appendChild(
89+ Y.Node.create('<input id="initval" value="foo" />'));
90+ picker.render();
91+ picker.show();
92+ Assert.isTrue(picker._search_input.get('value') == 'foo');
93+ },
94+
95 test_set_results_updates_display: function () {
96 this.picker.render();
97 var image_url = '../../lazr/assets/skins/sam/search.png';

Subscribers

People subscribed via source and target branches