The next step, part 2

56
The Next Step, Part 2

description

YUIConf 2010 talk about how to go from a simple widget to one that can do all sorts of tricks, all without cluttering up the simple base widget.

Transcript of The next step, part 2

Page 1: The next step, part 2

The Next Step, Part 2

Page 2: The next step, part 2

Where have we been?

Page 3: The next step, part 2

SimpleYUI, inline JS

Page 4: The next step, part 2

External JS file via <script>

Page 5: The next step, part 2

Plugin for Node objects

Page 6: The next step, part 2

Simple Widget

Page 7: The next step, part 2

Why continue?

Page 8: The next step, part 2

Then where are we going?

Page 9: The next step, part 2

Widget provides some structure

Page 10: The next step, part 2

initializer : function(config) { var node = this.get("node"); if(node) { if(!this.get("content")) { this.set("content", node.getAttribute("title")); } node.removeAttribute("title"); }},

destructor : function() { this._handlers.detach(); this._handlers = null;},

Page 11: The next step, part 2

renderUI

Page 12: The next step, part 2

bindUI

Page 13: The next step, part 2

bindUI : function() { var node = this.get("node"); this._handlers = node.on({ mouseenter : { fn : function() { this.get('boundingBox').setStyles({

left : e.pageX + 5, top : e.pageY + 5 }); this.show(); }, context : this }, mouseleave: Y.bind(this.hide, this) });},

Page 14: The next step, part 2

syncUI

Page 15: The next step, part 2

syncUI : function() { this.publish("sync", { defaultFn : Y.bind(function() { this.get('contentBox') .setContent(this.get('content'));

}, this) }).fire();}

Page 16: The next step, part 2

YUI().use("gallery-tooltip", function(Y) { var tooltip = new Y.Tooltip({ visible : false, render : true, node : Y.one("a") });});

Page 17: The next step, part 2

Now what?

Page 18: The next step, part 2

Plugins for new behaviors

Page 19: The next step, part 2

new Y.Tooltip({ visible : false, render : true, node : Y.one("a"), plugins : [ Y.Plugin.TooltipSimpleDisplay ]});

//orvar tooltip = New Y.Tooltip({ visible : false, render : true, node : Y.one("a")});tooltip.plug(Y.Plugin.TooltipSimpleDisplay);

Page 20: The next step, part 2

Simple Plugin

Page 21: The next step, part 2

Y.Base.create("TooltipSimpleDisplay", Y.Plugin.Base, [], { initializer : function() { var host = this.get("host"); host.on("sync", function(e) { //prevent default sync method e.preventDefault();

var content = host.get('content') + " + plugin";

host.get("contentBox").setContent(content); }, this); }}, { NS : 'TooltipSimpleDisplay' });

Page 22: The next step, part 2

Powered by Custom Events

That call to preventDefault is important

Page 23: The next step, part 2

syncUI : function() { this.publish("sync", { //Now we see why this slightly //odd syntax was important //it lets this method be //overriden by plugins defaultFn : Y.bind(function() { this.get('contentBox')

.setContent(this.get('content')); }, this) }).fire();}

Page 24: The next step, part 2

We can go further

http://www.flickr.com/photos/st3f4n/4501172754/

Page 25: The next step, part 2

Transitions Module

Adding some flash

Page 26: The next step, part 2

new Y.Tooltip({ visible : false, render : true, node : Y.one("a"), plugins : [ //from gallery Y.Plugin.TransitionOverlay ]});

Page 27: The next step, part 2

Still, not really all that useful

http://www.flickr.com/photos/zen/1174874997/

Page 28: The next step, part 2

WidgetIO

Page 29: The next step, part 2

YQL

Page 30: The next step, part 2

WidgetYQL

Page 31: The next step, part 2

Y.Base.create("WidgetYQL", Y.Plugin.Base, [], { _yql : null, initializer : function() { this.after([

"queryChange", "formatterChange", "configChange" ], this._createYQL, this); },_ _createYQL : function() { attrs = this.getAttrs([ "query", "formatter", "config" ]); this._yql = new Y.YQLRequest( attrs.query, Y.bind(attrs.formatter, this), attrs.config ); }, sendQuery : function() { return this._yql.send(); }

Page 32: The next step, part 2

tooltip.plug(Y.Plugin.WidgetYQL, { query : "SELECT * FROM ...", formatter : function(r) { host._yqlData = r.query.results.quote;

host.set("content", r.query...LastTradePriceOnly); host.fire("yqlResponse", r); }});

Page 33: The next step, part 2

Repeating yourself sucks

Page 34: The next step, part 2

Plugins are full JS objects

Page 35: The next step, part 2

Plugins on top of plugins

http://idont/have/a/source/its/just/funny

Page 36: The next step, part 2

initializer : function() { var host = this.get("host"); host.plug(Y.namespace("Plugin").WidgetYQL, { query : "SELECT * FROM ...", formatter : function(r) { host.set("content", r.query.res...); host.fire("yqlResponse"); } }

host.on(["visibleChange", "renderedChange"], function(e) { host.YQL.sendQuery(); }, this); host.on("yqlResponse", function() { host.syncUI(); });}

Page 37: The next step, part 2

Multiple plugins

Page 38: The next step, part 2

new Y.Tooltip({ visible : false, render : true, node : Y.one('a'), plugins : [ Y.Plugin.TransitionOverlay,

Y.Plugin.TooltipYQL, Y.Plugin.TooltipDisplay ]});

Page 39: The next step, part 2

Done? Not so fast.

Page 40: The next step, part 2

Prove it works, use tests

Page 41: The next step, part 2

Widget & YUITest

Page 42: The next step, part 2

var test = new Y.Test.Case({ name : "Simple Tooltip Test",

"tooltip should render without errors" : function() { var tooltip = new Y.Tooltip({ visible : false, render : true,

node : Y.one('a') }); }

});

//add & run testY.Test.Runner.add(test);Y.Test.Runner.run();

Page 43: The next step, part 2

Didn’t do anything too dumb

(yet)

Page 44: The next step, part 2

Not very useful though

http://www.flickr.com/photos/sewitsforyou/3466154372/

Page 45: The next step, part 2

Event simulation adds utility

Page 46: The next step, part 2

new Y.Test.Case({ name : "Tooltip Event Simulation Test", "tooltip should show on mouseenter" : function() { //create tooltip var tooltip = ... //simulate mousing over the link Y.one("a").simulate("mouseover"); //fail if the tooltip isn't visible Y.assert( tooltip.get("visible"), "Tooltip wasn't visible“ ); }});

Page 47: The next step, part 2

Lots of JS is async though

Sync tests don’t solve everything

Page 48: The next step, part 2

this.wait()

Page 49: The next step, part 2

this.resume()

Page 50: The next step, part 2

"tooltip should transition to visible" : function() { var tooltip = ... //resume the test once the transition has finished tooltip.transitionPlugin.on("end", function(visible) { this.resume(function() { Y.assert(visible && tooltip.get("visible"),

"Tooltip wasn't visible"); }); }, this); //show the overlay, triggering the transition tooltip.show(); //wait for a bit for the transition to end this.wait(1000);}

Page 51: The next step, part 2

Inline JS, great for prototyping

Bad for more complicated testing

Page 52: The next step, part 2

Scaling up the testing

Page 53: The next step, part 2

new Y.Test.Case({ name : "Advanced Tests", //set up a tooltip before every test, clean it up setUp : function() { ... }, tearDown : function() { ... }, //multiple tests "tooltip should render without errors" : function() { ... },

"tooltip should show on mouseenter" : function() { ... },

"tooltip should transition to visible" : function() { ... },

"YQL plugin should get data from YQL" : function() { ... }

});

Page 54: The next step, part 2

YUI Gallery

You’ve been meaning to share more anyways, right?

Page 55: The next step, part 2

One way to flesh out your idea

Definitely not the only one

Page 56: The next step, part 2

Patrick Cavit

@tivac on twitter

“tivac” in IRC

http://patcavit.com

http://lanyrd.com/people/tiva

c/