Sunday, August 31, 2014

NPM Script to Generate Polymer Test Setup


Up today, I continue my efforts to build something of a testing solution for Polymer elements. The built-in testing Dart solutions have been more than up to the task with Polymer.dart, but the JavaScript testing setup has proved trickier. To resolve the various annoyances, I have what I think is a relatively simple approach, but with some definite opinions. This seems ideal for extracting into a node.js package, if only to ease my own Polymer testing.

Last night, I extracted a bunch of the Karma configuration out into eee-polymer-tests. Now that it is a GitHub project, I can specify it as a development dependency in custom Polymer elements:
{
  "name": "parent-events",
  "devDependencies": {
    "eee-polymer-tests": "eee-c/eee-polymer-tests"
  }
}
After npm install (following the traditional rm -rf node_modules), I have Karma, Jasmine, and other testing requirements as well as settings from eee-polymer-tests:
$ rm -rf node_modules
$ npm install
karma-chrome-launcher@0.1.4 node_modules/karma-chrome-launcher
karma@0.12.23 node_modules/karma 
karma-jasmine@0.2.2 node_modules/karma-jasmine
eee-polymer-tests@0.0.1 node_modules/eee-polymer-tests
$ ls -l node_modules/eee-polymer-tests
total 16
-rw-r--r-- 1 chris chris 2022 Aug 31 01:06 karma-common.conf.js
-rw-r--r-- 1 chris chris 1078 Aug 31 01:06 LICENSE
-rw-r--r-- 1 chris chris 1340 Aug 31 22:02 package.json
-rw-r--r-- 1 chris chris  306 Aug 31 01:06 README.md
Thanks to NPM peer dependencies, the Karma and Jasmine packages are installed for easy use in my element's tests. Thanks to the karma-common.conf.js from last night, my own karma.conf.js is nothing more than:
module.exports = function(config) {
  var common = require('./node_modules/eee-polymer-tests/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // ...
  }));
};
With no other setup required, my tests still pass:
$ karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket B3i3n_dnKrvjoYei80Jc with id 60115395
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.076 secs / 0.072 secs)
That is pretty good, but I would like to be able to generate some of the testing code that is currently required to make all of this work:
.
├── bower.json
├── elements
│   └── click-sink.html
├── index.html
├── karma.conf.js
├── package.json
└── test
    ├── ClickSinkSpec.js
    └── PolymerSetup.js
The bower.json file exposes a hidden dependency on Bower. The only real consideration for Bower is that my tests expect Polymer dependencies to be installed in bower_components. I will defer consideration of that until another day.

For now, I would like to write a script that creates the other three files in the list. I start with test/PolymerSetup.js, which is nearly boilerplate except for the inclusion of the main Polymer element at the very end of the file:
// Delay Jasmine specs until Polymer is ready
var POLYMER_READY = false;
beforeEach(function(done) { /* wait for polymer-ready */ });

// 1. Load Polymer before any code that touches the DOM.
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

// 2. Load component(s)
var link = document.createElement("link");
link.rel = "import";
link.href = "/base/elements/click-sink.html";
document.getElementsByTagName("head")[0].appendChild(link);
I am unsure if there are any standard templating solutions for Node.js (a quick search did not turn one up), so regular expressions it is!

I create a templates/PolymerSetup.js file in eee-polymer-test that is identical to the current setup from my working tests. The only change is a mustache-like placeholder for the element name:
// Identical up to...
// 2. Load component(s)
var link = document.createElement("link");
link.rel = "import";
link.href = "/base/elements/{{element-name}}.html";
document.getElementsByTagName("head")[0].appendChild(link);
I would prefer that eee-polymer-tests be installed globally (i.e. with npm install -g), but that is not going to be an option because of last night's peer dependencies. Instead, this will be run as:
$ ./node_modules/eee-polymer-tests/cli.js click-sink
Given that execution path, the template path will be ./node_modules/eee-polymer-tests/templates/PolymerSetup.js. There is probably a way to determine this path relative to the currently executing script, but I will leave that for another day.

I just want to be able to read the template and write the template with the element applied. The read should look something like:
#!/usr/bin/env node

var element = process.argv[2];

var fs = require('fs');
fs.readFile('./node_modules/eee-polymer-tests/templates/PolymerSetup.js', 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }
  writeSetup(data);
});
The writeSetup() can then do the opposite with writeFile():
function writeSetup(template) {
  var content = template.replace(/\{\{element-name\}\}/g, element);
  fs.writeFile('./test/PolymerSetup.js', content);
}
That takes care of globally replacing the {{element-name}} template placeholder (thanks to the g modified on the regular expression).

And that seems to do the trick. I run that code—overwriting the current PolymerSetup.js—and re-run the tests to find everything still passing.

This seems quite promising for my needs. I am unsure if anyone else will find it similarly useful, but I am definitely filling a need of my own here. I think tomorrow I will continue with this, adding in some overwrite protection. That aside, this is already quite close to what I need.


Day #169

Saturday, August 30, 2014

A Package for Common Polymer Testing


I was unable to get polymer-test-tools to work well with a custom Polymer of my own. The effort was not completely wasted as I learned a ton. The most interesting thing that I learned is that I have some definite opinions on what ought to be included in a general purpose Polymer testing tool. Enough, possibly, to write my own.

My Polymer element setup usually looks something like:
.
├── test
│   ├── PolymerSetup.js
│   └── ClickSinkSpec.js
├── package.json
├── karma.conf.js
├── index.html
└── elements
    └── click-sink.html
I have my element(s) in the elements subdirectory, the tests in the tests subdirectory, a package.json that includes Karma and Jasmine dependencies and a Karma configuration file. Also of note is is that I have very specific test setup that goes into test/PolymerSetup.js to enable testing with Polymer.

For a first pass at a testing tool (generator? template? not sure yet?), I initialize a git and npm repository:
➜  repos  mkdir eee-polymer-tests
➜  repos  cd !$
➜  repos  cd eee-polymer-tests
➜  eee-polymer-tests  git init
Initialized empty Git repository in /home/chris/repos/eee-polymer-tests/.git/
➜  eee-polymer-tests git:(master) npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install  --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (eee-polymer-tests)
version: (1.0.0) 0.0.1
description: Sane Polymer test setup (including generator)
entry point: (index.js)
test command:
git repository: https://github.com/eee-c/eee-polymer-tests
keywords: polymer testing karma jasmine
author: Chris Strom
license: (ISC) MIT
To build this up, I would like to slowly replace parts in my working tests with this NPM package. Unfortunately, I do not know how to do this quickly with NPM since it does not support local filesystem dependencies. Well, that's not entirely true, NPM does support npm link, but I am unsure how to get that to install dependencies.

And, in the case of eee-polymer-tests, I need to include karma-jasmine and karma-chrome-launcher as peer dependencies. I specify those in my package.json:
{
  "name": "eee-polymer-tests",
  // ...
  "peerDependencies": {
    "karma-jasmine": "~0.2.0",
    "karma-chrome-launcher": ">0.0"
  }
}
As peer dependencies, these will be installed in my Polymer application the same as if I had listed them as direct dependencies (or development dependencies):
{
  "name": "parent-events",
  "devDependencies": {
    "karma-jasmine": "~0.2.0",
    "karma-chrome-launcher": ">0.0"
  }
}
The only way I can get my Polymer application to honor my local package's peer dependencies—at least the only way that I can figure out tonight—is to add my local package as a pre-install script to my Polymer element's package.json:
{
  "name": "parent-events",
  "scripts": {
    "preinstall": "npm install /home/chris/repos/eee-polymer-tests/"
  }
}
That actually seems to work:
$ npm install
> parent-events@ preinstall /home/chris/repos/polymer-book/book/code-js/parent_events
> npm install /home/chris/repos/eee-polymer-tests/
...
karma-chrome-launcher@0.1.4 node_modules/karma-chrome-launcher

karma@0.12.23 node_modules/karma
└── ...

karma-jasmine@0.2.2 node_modules/karma-jasmine

eee-polymer-tests@0.0.1 node_modules/eee-polymer-tests
And...
$ ls node_modules
eee-polymer-tests  karma  karma-chrome-launcher  karma-jasmine
Most importantly, my Karma tests still pass:
karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket wzCHNN5LnCOMY2-V4ELH with id 70589859
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.068 secs / 0.061 secs)
Next up, I need to borrow from a trick I learned from polymer-test-tools. I need to move my common testing setup for karma into the NPM package, but still allow developers a means to override the common stuff. Directly from polyer-test-tools, I create karma-common.conf.js in the eee-polymer-tests NPM package:
exports.mixin_common_opts = function(karma, opts) {
  var all_opts = {
    //  Common settings go here...
  };
  for (var key in opts) {
   all_opts[key] = opts[key];
  }
  return all_opts;
};
The common setting are mostly the defaults from the Karma generator. The ones specific to my preferred Polymer testing setup include the testing framework to use:
    // frameworks to use
    frameworks: ['jasmine'],
A means to facilitate fixure loading if needed:
    /**
     * Compile HTML into JS so that they can be used as templates
     */
    preprocessors: {
      'test/*.html': 'html2js'
    },
And the list of files to include for testing or to serve (but not directly execute):
    /**
     * Don't include Polymer HTML and JS because Polymer is very
     * particular about the order in which they are added. Serve them,
     * but defer loading to the test setup. Include test HTML
     * fixtures.
     */
    // list of files / patterns to load in the browser
    files: [
      'test/PolymerSetup.js',
      {pattern: 'elements/**', included: false, served: true},
      {pattern: 'bower_components/**', included: false, served: true},
      'test/**/*Spec.js'
    ],
Back in my Polymer element, the karma.conf.js then becomes simply:
module.exports = function(config) {
  var common = require('./node_modules/eee-polymer-tests/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // Specialization could go here, but none needed!
  }));
};
I do not need to override any of the default settings. Furthermore, I think the common options would likely suit just about any elements that I have created so far. Famous last words, I am sure.

Anyhow, trying it out, I find:
$ karma start --single-run
ERROR [config]: Error in config file!
 { [Error: Cannot find module './node_modules/eee-polymer-tests/karma-common.conf.js'] code: 'MODULE_NOT_FOUND' }
Error: Cannot find module './node_modules/eee-polymer-tests/karma-common.conf.js'
    at Function.Module._resolveFilename (module.js:338:15)
    ...
This is a definite drawback to the pre-install NPM approach. I have to re-install each time I want to try something new. That is going to be a pain if I need to debug often. Perhaps if that is the case, I can try a hybrid approach that first installs dependencies, but then npm-links my local package. Food for thought another day.

For now, I re-install:
$ rm -rf node_modules
$ npm install
...
eee-polymer-tests@0.0.1 node_modules/eee-polymer-tests
With that:
karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket Q1jxhuNWJtv6SKmi2C2L with id 12087020
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.057 secs / 0.051 secs)
Nice!

I now require only a single testing dependency and zero configuration. That may not be completely reusable by others, but it does afford a certain amount of configuration thanks to the approach from the polymer-test-tools project. Regardless, it is a good start and will help me very much in my efforts to test the remainder of the elements in Patterns in Polymer.

I have not saved myself any files required for testing. I may or may not be able to include the PolymerSetup.js directly in eee-polymer-tests. I have been including the <link> import of the element definition under test in PolymerSetup.js. It is possible that I can move the import elsewhere, but that will take some experimentation. I will always need the equivalent of ClickSinkSpec.js. Now that I think about it, both could be generated from a script in eee-polymer-tests.

I will work on that tomorrow.


Day #168

Friday, August 29, 2014

Testing Custom Polymer Elements with Polymer-Test-Tools


I discovered polymer-test-tools last night, but could not quite get them working. While exploring, I began to suspect that they were meant more to support the Polymer project's infrastructure than to help “regular” programmers build elements. Even so, I would like to get them working with my Polymer element, if only to see what polymer-test-tools can teach me about my own testing approach.

I left off last night with a strange SSL / socket.io error. It seemed like an old node.js, but it turns out that I have a relatively recent version of node installed. And, in fact, I no longer see the error tonight. I swear that I have done nothing that could possibly have fixed this, except… it is fixed. Rather than question fortune, I say a prayer of thanks to the gods of the chain and quickly move on before they change their mind.

Not all is well just yet, but I am quite familiar with the next errors thanks to earlier experiments testing Polymer with mocha and chai. First up the before-each failure on start-up:
$ karma start
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
Chrome 37.0.2062 (Linux)  "before each" hook FAILED
        Error: timeout of 2000ms exceeded
Chrome 37.0.2062 (Linux) SLOW 2.003 secs:  "before each" hook
Chrome 37.0.2062 (Linux): Executed 1 of 2 (1 FAILED) ERROR (2.037 secs / 2.003 secs)
I fix this with the same solution from the last time—test setup that relies on Polymer.whenReady() instead of the too-quick polymer-ready event:
// Delay Jasmine specs until Polymer is ready
var POLYMER_READY = false;
beforeEach(function(done) {
  function waitForPolymer() {
    if (Polymer) {
      Polymer.whenReady(done);
      return;
    }
    setTimeout(waitForPolymer, 1000);
  }
  waitForPolymer();

  if (POLYMER_READY) done();
});
I had expected that to be the last of my woes, but I still have another sadly familiar error message to fix:
$ karma start
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket sgWf2bLuHEiLYy7BxgN0 with id 56442994
Chrome 37.0.2062 (Linux)  element content displays click events FAILED
        ReferenceError: expect is not defined
            at Context.<anonymous> (/home/chris/repos/polymer-book/book/code-js/parent_events/test/ClickSinkSpec.js:23:7)
I ran into this same error when I setup mocha on my own, but am surprised to see it with polymer-test-tools. The problem is that the chai framework is not loaded. I am surprised by this because the chai framework is included directly in the polymer-test-tools source. Why include it in the source if it is not going to be enabled by default? I can only guess that the answer has to do with using polymer-test-tools in the Polymer framework code rather than testing custom built Polymer elements.

Whatever the reason, the solution remains the same—I have to alter my karma.conf.js file to include chai in addition to mocha (which polymer-test-tools does include by default):
module.exports = function(config) {
  var common = require('./bower_components/polymer-test-tools/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    frameworks: ['mocha', 'chai'],
    // ...
  }));
};
Only this time, my problem is still not resolved. Since I overrode polymer-test-tools' plugins setting last night, I also have to add karma-chai to it here:
module.exports = function(config) {
  var common = require('./bower_components/polymer-test-tools/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    frameworks: ['mocha', 'chai'],

    plugins: [
      'karma-mocha',
      'karma-chai',
      'karma-chrome-launcher',
      'karma-script-launcher'
    ],
    // ...
  }));
};
After that, all that remains is to change Jasmine's toEqual() and toContain() to chai's to.equal() and to.contain():
  describe('element content', function(){
    beforeEach(function(done){
      document.body.click();
      setTimeout(done, 0); // One event loop for Polymer to process
    });
    it('displays click events', function(){
      expect(el.shadowRoot.textContent).to.contain('Page clicked');
    });
  });
With that, I have all (both) of my tests passing under polymer-test-tools:
$ karma start
...
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.196 secs / 0.003 secs)
In the end, this really saved me no work. In fact, I had to do a little extra work to disable settings that were not relevant to my on my Linux machine (e.g. IE, Safari testing support). Since I still have to manually install mocha and chai (and setup chai) despite both being included in the polymer-test-tools repository, I save no effort. In other words, this is not a tool for testing custom Polymer elements. Polymer-test-tools is a place for common framework settings to go.

Still, the effort was hardly a complete waste. It is instructive seeing how the Polymer team tests different platforms and browsers (conditionals depending on the operating system). The overall approach seems similar to what I have been using, though I probably ought to investigate a bit more before claiming confirmation of my approach.

Bottom line: I plan to stick with my current approach in Patterns in Polymer.


Day #167

Thursday, August 28, 2014

Getting Started with Polymer-Test-Tools


I can whine for long periods of time about how much better Dart testing is than JavaScript. Loooong periods. But let's face it, that is hardly constructive and, worse, it ignores the hard work of talented folks that are working to make things better.

Last night, I added some simple Dart tests to one of the chapters in Patterns in Polymer. The primary element in that chapter (both the Dart and JavaScript versions of the book) is <click-sink> which, when embedded in another element, will report on events in the containing element. The Dart tests were easy to add, so tonight I do the same in JavaScript.

Until now, my testing solution for JavaScript Polymer has been Karma and Jasmine. It takes a little more work to get these tests working: package.json to install Karma dependencies via NPM, bower.json to install Polymer dependencies via Bower, Jasmine setup for Polymer, etc. In all honesty, it's not that much harder to setup than in Dart.

I would note that the tests just are not as pretty:
  beforeEach(function(done){
    el = document.querySelector('click-sink');
    setTimeout(done, 0); // One event loop for elements to register in Polymer
  });

  describe('element content', function(){
    beforeEach(function(done){
      document.body.click();
      setTimeout(done, 0); // One event loop for Polymer to process
    });
    it('displays click events', function(){
      expect(el.shadowRoot.textContent).toContain('Page clicked');
    });
  });
I really am partial to scheduled_test's schedules:
  test('displays click events', (){
    var el = query('click-sink');
    schedule(()=> document.body.click());
    schedule(() {
      expect(el.shadowRoot.text, contains('Page clicked'));
    });
  });
But I'm not whining!

Instead, I went in search of some other examples of Polymer testing in JavaScript and stumbled across polymer-test-tools, which is a project from the Polymer team itself that has “common tools for testing Polymer elements.” It uses Karma, but instead of Jasmine, it uses Mocha and Chai. I have already investigated both as testing solutions. I remain open to the switch, so let's see if polymer-test-tools can convince me.

It is non-obvious how to use polymer-test-tools, but it has a bower.json, suggesting that I need to add it to my bower.json development dependencies:
$ bower install -D "Polymer/polymer-test-tools"
...
polymer-test-tools#0.4.0 bower_components/polymer-test-tools
Next, I update my karma.conf.js file to pull in the settings from polymer-test-tools:
module.exports = function(config) {
  var common = require('./bower_components/polymer-test-tools/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // Override with my setting here...
  }));
};
I retain some of my own settings—especially the list of the test files—but remove most of what is listed in the common karma configuration. But when I run this, I get warnings about plugins that are not installed:
$ karma start --single-run
WARN [plugin]: Cannot find plugin "karma-mocha".
  Did you forget to install it ?
  npm install karma-mocha --save-dev
WARN [plugin]: Cannot find plugin "karma-browserstack-launcher".
  Did you forget to install it ?
  npm install karma-browserstack-launcher --save-dev
WARN [plugin]: Cannot find plugin "karma-ie-launcher".
  Did you forget to install it ?
  npm install karma-ie-launcher --save-dev
WARN [plugin]: Cannot find plugin "karma-ios-launcher".
  Did you forget to install it ?
  npm install karma-ios-launcher --save-dev
WARN [plugin]: Cannot find plugin "karma-safari-launcher".
  Did you forget to install it ?
  npm install karma-safari-launcher --save-dev
WARN [plugin]: Cannot find plugin "karma-crbot-reporter".
  Did you forget to install it ?
  npm install karma-crbot-reporter --save-dev
I have no desire for most of those, so I override them in my settings:
module.exports = function(config) {
  var common = require('./bower_components/polymer-test-tools/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // ...
    plugins: [
      'karma-mocha',
      'karma-chrome-launcher',
      'karma-script-launcher'
    ]
  }));
};
That still complains about a lack of karma-mocha:
$ karma start --single-run
WARN [plugin]: Cannot find plugin "karma-mocha".
  Did you forget to install it ?
  npm install karma-mocha --save-dev
I had hoped to get this automatically from polymer-test-tools since it includes both mocha and chai:
$ ls bower_components/polymer-test-tools/     
bower.json  chai  htmltest.js  karma-common.conf.js  mocha  mocha-htmltest.js  README.md  tools.html
Alas, this is not the case.

After following the instructions (npm install karma-mocha --save-dev), I run into another problem:
$ karma start
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
ERROR [karma]: [Error: error:24064064:random number generator:SSLEAY_RAND_BYTES:PRNG not seeded]
Error: error:24064064:random number generator:SSLEAY_RAND_BYTES:PRNG not seeded
    at Manager.generateId (/home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:750:12)
    at /home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:805:21
    at Manager.authorize (/home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:931:5)
    at Manager.handleHandshake (/home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:801:8)
    at Manager.handleRequest (/home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:616:12)
    at Server.<anonymous> (/home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/karma/node_modules/socket.io/lib/manager.js:119:10)
    at Server.EventEmitter.emit (events.js:98:17)
    at HTTPParser.parser.onIncoming (http.js:2076:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:120:23)
    at Socket.socket.ondata (http.js:1966:22)
Yikes. Apparently, this is a known issue with Karma. Unfortunately, it looks like I need to install a new node.js. Sigh.

I will call it a night here and maybe pick back up with this tomorrow. While working through this, I am starting to get the feeling that polymer-test-tools is not a general purpose Polymer testing helper. It seems like it helps quite a bit with testing Polymer internals like Shadow DOM, but it may not be appropriate for coders building and maintaining their own Polymer elements. Even so, I may pick back up with this if, for no other reason, than to learn a little from the smart folks behind Polymer.


Day #166

Wednesday, August 27, 2014

When to TDD and When Not to TDD (an exercise with Polymer.dart)


According to git lslt, it has been six months since I last updated the parent events chapter in Patterns in Polymer. Six months!

I start my review, as always, with my tests:
$ pwd     
/home/chris/repos/polymer-book/book/code-dart/parent_events
$ ls -l test
total 0
lrwxrwxrwx 1 chris chris 11 Dec  7  2013 packages -> ../packages
Son of a… Bad developer! Bad author!

I will not produce another edition without complete test coverage of the examples in the book. I will not worry about the code in the “play” area of the repository—that is mostly meant for spiking new features or exploring new facets of Polymer. But there is no excuse for basing any programming book on top of code without tests. So, I start with…

Running the application. It's silly to start testing without some idea of what things look like, so I double-check that this still runs. I will worry about the tests after the fact (not everything in life requires TDD). First, I clean out the pubspec.yaml dependencies of some early-Polymer cruft, leaving me with simply:
name: parent_events
dependencies:
  polymer: any
dev_dependencies:
  scheduled_test: any
I pub upgrade and find:
$ pub upgrade
Resolving dependencies... (4.4s)
...
> polymer 0.12.0 (was 0.9.0+1) (8 newer versions available)
...
Changed 29 dependencies!
Craziness. I cannot believe that I still have Polymer.dart 0.9 code the book (the 8 newer versions are pre-releases).

I also update the smoke test page in the Dart Pub standard web directory to perform the proper setup:
<!doctype html>
<html lang="en">
  <head>
  <!-- ... -->
    <!-- Load platform polyfills -->
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>

    <!-- Load component(s) -->
    <link rel="import" href="packages/parent_events/click-sink.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
  <!-- ... -->
</html>
That should do it. So I fire up Dart pub's built-in test web server (pub serve), access the page, click around, and… nothing:



If I recall correctly, the <click-sink> element added to the page should listen for page events. With another 6 months of Polymer under my belt, I am no longer sure this is a strong pattern, but I would at least like to get it working.

Saaaaaay... this is a good time to introduce some tests! In the pub standard test directory, I add the text page context:
<!doctype html>
<html>
<head>
  <!-- Load platforms polyfills -->
  <script src="packages/web_components/platform.js"></script>

  <!-- Load component(s) -->
  <link rel="import" href="packages/parent_events/click-sink.html">

  <!-- The actual tests -->
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
  <click-sink></click-sink>
</body>
</html>
That is similar to the regular Polymer.dart page, but to initialize and test Polymer elements, I have found this to be an effective approach. I normally create the Polymer element inside my test setup, but I anticipate these tests to be simple enough that I add <click-sink> directly to the page. I can always change later if the need arises.

In addition to the test page context, I also need to initialize Polymer inside my tests:
library click_sink_test;

import 'package:polymer/polymer.dart';
import 'package:scheduled_test/scheduled_test.dart';

main() {
  initPolymer();

  setUp((){
    schedule(()=> Polymer.onReady);
  });

  test('displays click events', (){
    expect(false, true);
  });
  test('generates click-sink-click events', (){
    expect(false, true);
  });
}
And look! I have two failing tests already in place. I could access my tests from the pub web server (which spins up a test server as well), but I prefer testing with content_shell for ease of transition to continuous integration testing. The arguments are arcane, but as I avowed Linux user, I am used to it:
$ content_shell --dump-render-tree test/index.html
CONSOLE WARNING: line 12: flushing %s elements
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: FAIL: displays click events
  Caught ScheduleError:
  | Expected: <true>
  |   Actual: <false>

CONSOLE MESSAGE: FAIL: generates click-sink-click events
  Caught ScheduleError:
  | Expected: <true>
  |   Actual: <false>

CONSOLE MESSAGE:
CONSOLE MESSAGE: 0 PASSED, 2 FAILED, 0 ERRORS
My first step is right out the TDD playbook: change the (error) message. To do so, I write an actual test:
  test('displays click events', (){
    var el = query('click-sink');
    schedule(()=> document.body.click());
    schedule(() {
      expect(el.shadowRoot.text, contains('Page clicked'));
    });
  });
When I click the document body, I expect that the <click-sink> Polymer element will contain a text message that includes the coordinates of the click. Running this test, I find that the message has indeed changed:
CONSOLE MESSAGE: FAIL: displays click events
  Caught ScheduleError:
  | Expected: contains 'Page clicked'
  |   Actual: '\n'
  |   '    \n'
  |   '      Click Anywhere on the Page\n'
  |   '      \n'
  |   '        \n'
  |   '      \n'
  |   '    \n'
  |   '  '
Now, why on earth is this failing?

It turns out that I am using old syntax for the Polymer attached life cycle method (that tends to happen with a 6+ month old dependency on an alpha state library):
@CustomTag('click-sink')
class ClickSinkElement extends PolymerElement {
  // ...
  ClickSinkElement.created(): super.created();
  enteredView() {
    super.enteredView();
    // ...
  }
}
Both Polymer.dart and JavaScript Polymer have long since standardized on the attached() callback method (called when the Polymer instance is attached to the container document). So the fix is simple enough:
@CustomTag('click-sink')
class ClickSinkElement extends PolymerElement {
  // ...
  ClickSinkElement.created(): super.created();
  attached() {
    super.attached();
    // ...
  }
}
And, just like that, I have my element working and a legitimate test to ensure that something like this never happens again:
CONSOLE MESSAGE: PASS: displays click events
I remain quite disappointed in myself for not having done this already. Hopefully this is a case of better (severely) late than never.


Day #165

Tuesday, August 26, 2014

Git Subcommand to Sort by Last Modified Date


OK, it's time to get serious about updating Patterns in Polymer and finishing the screencasts. But where to start? I know what needs to be done for the screencasts: record them (the upcoming long US weekend should help there). But how best to focus my efforts on the chapters in the book?

Working through each chapter based on last modified date seems the best bet, but all of the chapter files on my filesystem have the same last modified timestamp:
$ ls -l *asc
...
-rw-r--r-- 1 chris chris 13163 Jun 28 10:06 i18n.asc
-rw-r--r-- 1 chris chris  5213 Jun 28 10:06 live_reload.asc
-rw-r--r-- 1 chris chris 10713 Jun 28 10:06 mdv.asc
-rw-r--r-- 1 chris chris  7917 Jun 28 10:06 plain_old_forms.asc
-rw-r--r-- 1 chris chris 12495 Jun 28 10:06 polymer.asc
-rw-r--r-- 1 chris chris   794 Jun 28 10:06 strategy.asc
Either I made a very large commit on June 28 at 10:06 or that was the last time that I checked these files out fresh (either from a new clone or switching from a sparse branch).

In fact, the last time that I updated the i18n chapter was back in April (yikes!):
$ git log i18n.asc | head -5
commit 030968dff38794ba7e20fa3ae839fc62a999eee2
Author: Chris Strom 
Date:   Sun Apr 20 23:39:09 2014 -0400

    Fix up i18n copy and code
So how do I go about sorting by the last-modified-in-git date? That turns out to be tricky. Thankfully, there is a nice Stack Overflow post on the subject.

The date timestamp from that article will not help sorting, but ISO 8601 will. Happily, git-log supports logging with ISO 8601:
$ git log -1 --date=iso --format="%ad" i18n.asc 
2014-04-20 23:39:09 -0400
It have the feeling that I would like to use this again, so I will create a git subcommand: git-lslt (similar to the usual ls -lt). Git subcommands are trivial—I need only a script prefixed with git-. So I create $HOME/bin/git-lslt as:
#!/bin/sh

ls | \
  while read filename; do
    echo "$(git log -1 --date=iso --format="%ad" -- $filename) $filename"
  done | \
  grep '^2' | \
  sort
Most of that is from the Stack Overflow article. The sort is self-explanatory. The grep '^2' selects only entries with date stamps—meaning that files not tracked by git will be ignored.

The output looks like:
$ git lslt
2013-11-16 16:26:38 -0500 about.asc
2013-11-16 16:26:38 -0500 chapter_1.asc
2013-11-16 17:51:48 -0500 getting_help.asc
2014-01-14 19:58:38 -0500 book_dart.asc
2014-01-14 19:58:38 -0500 book_js.asc
2014-02-12 02:52:49 -0500 copyright.asc
...
Most of that is boilerplate code that never needs an update. I suspect that the "about" file has since been absorbed into the introduction and can be deleted.

After grepping the output a bit more, I find a few surprises:
$ git lslt | \
   grep .asc | \
   grep -v 'chapter_1\|contents\|copyright\|book.*.asc\|bibliography'
...
2014-02-14 01:13:27 -0500 parent_events.asc
2014-02-15 01:16:04 -0500 parent_child.asc
2014-02-15 01:16:04 -0500 svg.asc
2014-02-15 16:12:16 -0500 configuration.asc
...
I am surprised that the parent events and parent-child chapters have not been updated. I could have sworn that I got (and incorporated) feedback on at least one of those. The configuration chapter has since been removed (possibly permanently), so that is no surprise. But I have learned a ton about SVG since February—I am quite surprised that I have not included any of that knowledge in that chapter.

My new git lslt subcommand has already paid some nice dividends. I know where I need to focus my initial update/rewrite efforts. Starting tomorrow.


Day #164

Monday, August 25, 2014

At the Bottom of the Rabbit Hole, I Find Polyfill-Next-Selector


The more I investigate styling and theming Polymer elements, the less a grasp I feel like I have on it. The core-style continues to vex. Beyond that, I am also having difficulty applying consistent CSS styles on my elements.

I am unsure if my current problems are with Polymer or with the Core Elements and Paper Elements built on top of Polymer. The most recent issue that I find is that the <paper-button> inside of a sample Polymer element that I am trying to create do not share styles. Specifically, the border is not included on all paper-buttons:



Both the top buttons and the dialog buttons are <paper-button> elements. Both should be covered by my core-style CSS:
<polymer-element name="lorem-dialog">
  <template>
    <paper-button label="Bottom" on-tap="{{toggleDialog}}"></paper-button>
    <paper-button label="Center" on-tap="{{toggleDialog}}"></paper-button>

    <paper-dialog heading="Bottom Dialog" transition="paper-dialog-transition-bottom">
      <!-- ... -->
      <paper-button label="Accept" affirmative autofocus></paper-button>
    </paper-dialog><
    <!-- ... -->
    <core-style id="theme">
      :host /deep/ paper-button {
        color: {{g.primaryColor}};
        border: 5px solid {{g.secondaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="theme"></core-style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
The core-style CSS is applied with the /deep/ CSS modifier. My understanding of /deep/ is that my styles should penetrate any shadow DOMs that are under the current element. And it kinda/sorta does what I expect. The primary color, themed from the containing page, is set to orange and it does apply to all paper-button elements. But darn it, why are the borders not in place for the paper-buttons in the dialogs?

My first thought is that perhaps this is a native shadow DOM vs. polyfill issue, so I load the demo page up in Firefox. This only makes things worse:



The borders are applied to all paper-buttons, but none of the core-style global settings are in place. Bleh.

Taking a step back, I know from last night that core-style does not like the way that I am using it. I take that out of the equation by removing the id/ref core-style pair of tags, replacing them with a straight forward <style> tag:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green;
        padding: 2px 5px;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
That solve my Firefox problem. The polyfills now correctly style my paper-button elements:



But this remains unchanged in Chrome (stable through unstable). On the surface, it would seem that Polymer styles are just broken at this point, but I refuse to indulge in select-is-broken thoughts. At least not yet.

If I peek at one of the paper-button elements in the Chrome inspector, I find that my border style is being overridden:



But what is that double-colon content thingy? I have no idea how that applies to my paper-button elements, let alone is more specific (in a CSS selector sense) than my /deep/ paper-button selector. Furthermore, I am hindered by the odd lack of file and line number for this selector. How can I understand and work around the darn thing if I cannot even find it?

To the ack machine Robin!

Amazingly, that actually finds something:
$ ack -l ::content                                                  
bower_components/platform/platform.js
bower_components/platform/platform.js.map
bower_components/paper-dialog/paper-dialog.css
bower_components/core-style/elements.html
bower_components/core-component-page/core-component-page.html
Only one of those is a CSS file and just so happens to be the element (paper-dialog) that is wrapping my misbehaving paper-buttons. Taking a look at the offending CSS, I find:
polymer-next-selector { content: ':host > *'; }
::content > * {
  font: inherit;
  border: 0;
}
That is exactly the definition of the culprit that I found in the DOM inspector. A quick review of the other matching files reveals that none of them have the same style definition. So the question becomes… what the heck is a polymer-next-selector? More importantly, how do I override it?

The answer to the first question seems to be that polymer-next-selector is also known as polyfill-next-selector. It seems to be a way to apply styles in the polyfills, but which work natively with the shadow DOM if present. Only, the precedence on these things is crazy high.

Try as I might, I cannot target the distributed nodes of the paper-dialog from my custom element that tries to use paper-dialog. I am surprised that targeting the distributed nodes of paper-dialog has no effect:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green;
        padding: 2px 5px;
      }

      paper-dialog::content > * {
        border: 5px dashed blue;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
In the end, I give up trying to work with ::content and simply add an !important to the border style:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green !important;
        padding: 2px 5px;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
That does the trick:



But it feels like a cheat.

I cannot figure out why the ::content distributed node selector has such high precedence or why my attempts to influence it failed. I am probably overlooking some selector combinator that would allow me to target paper-button distributed into paper-dialog, but I wish it were easier to find that combinator. And if there is no easier way to do this, then why is paper-dialog using such a high precedence selector to ensure that none of its nodes have borders?

I have a solution, but I have more questions than answers to go along with that solution. Which makes this feel like a cheat.




Day #163

Sunday, August 24, 2014

Crazy Theming Polymer.dart with Core-Style


I am resigned.

Resigned that the core-style element and I disagree on how it should support theming. I have no illusions that I am in the right on this one, but I will attempt to make my case anyway.

When I think of custom themes, I think of setting a few colors and maybe a few other atomic values that can be used by a widget to look more or less like it fits in with the rest of the web page. In core-style, parlance, this looks like:
      <script>
        CoreStyle.g.primaryColor = 'orange';
        CoreStyle.g.secondaryColor = 'green';
      </script>
Or, alternatively, as:
        CoreStyle.g = {
          primaryColor: 'orange',
          secondaryColor = 'green'
        };
I would think that web page trying to use a widget would set these values and the widget would then honor the values. As best I can tell, this is not how core-style works.

Instead, core-style wants these global settings defined by the containing page and it wants the containing page to define styles that will apply to the internals of the widgets being used. That is, if I know that my <lorem-dialog> custom Polymer element contains paper-button elements, then core-style would have me define the global property setting and the detailed widget styling on the containing page:
  <body>
    <h1>Play Paper</h1>
    <lorem-dialog></lorem-dialog>
    <core-style id="theme">
      :host paper-button {
        border: 5px solid {{g.primaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <script>
      CoreStyle.g.primaryColor = 'orange';
    </script>
  </body>
I cannot help but think this requires the containing page to know too much about the internal implementation of the widget (the Polymer element).

I realize that I am almost certainly making a dumb assumption here. I am probably abusing this. Or not thinking right. Knowing this does not help me. Making the problem worse usually does. If I push this further—feel more pain in coding the wrong way—then I usually figure out how to do it right.

Last night, I was able to get core-style to work like I want by making the core-style's g property available in the backing Polymer element class:
Polymer('lorem-dialog', {
  created: function() {
    this.g = CoreStyle.g;
  },
  // ...
}
I could then use this reference inside the Polymer template:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <core-style id="theme">
      :host paper-button {
        border: 5px solid {{g.primaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="theme"></core-style>
  </template>
  <script>
    Polymer('lorem-dialog', { /* ... */ });
  </script>
</polymer-element>
Unfortunately, that does not work in Polymer.dart. Try as I might, there is no g property on the core-style object in Dart. But…

I know that the Dart versions of the core and paper elements are just thin wrappers around the JavaScript versions. Since I know how to get the global core-style values from JavaScript, perhaps I can use Dart's awesome JavaScript interoperability to resolve this dilemma.

So, I import the dart:js packages and define a g getter to return the value of CoreStyle.g from JavaScript:
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'dart:js';

@CustomTag('lorem-dialog')
class LoremDialog extends PolymerElement {
  LoremDialog.created(): super.created();

  get g => context['CoreStyle']['g'];
  // ...
}
And that actually works. In the template, I can define and reference a “theme” style that makes use of the global values from the web page using my Polymer element:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <core-style ref="theme"></core-style>
    <core-style id="theme">
      paper-button {
        border: 5px solid {{g['secondaryColor']}};
        color: {{g['primaryColor']}};
        font-weight: bold;
        padding: 2px 5px;
      }
    </core-style>
  </template>
  <script type="application/dart" src="lorem_dialog.dart"></script>
</polymer-element>

I strongly doubt that I will offer this up as a solution in Patterns in Polymer, but it does work in both JavaScript and Dart. Even so, the amount of resistance that I get from the library is strongly suggesting to me that I am doing something wrong. But even pushing this hard against core-style is not suggesting to me the right way to tackle this problem. Hopefully something will come to me (or someone can point me in the right direction) over the course of the next few days...


Day #162

Saturday, August 23, 2014

Where Core-Style Global Properties Can Be Used


I am struggling with the core-style element in Polymer.dart. I can implement simple styles—both as a source and referencing them later. But I have not been able to get arguably the coolest feature working yet: setting group variables.

For instance, I might want to theme some elements with the primary color of orange. From the container page, I ought to be able to set this in a script like so:
    <script>
      CoreStyle.g.primaryColor = 'orange';
    </script>
Custom Polymer elements should then honor this primary color with settings like:
<link rel="import"
      href="../../../packages/polymer/polymer.html">
<!-- ... -->
<link rel="import"
      href="../../../packages/core_elements/core_style.html">
<polymer-element name="lorem-dialog">
  <template>
    <paper-button label="Bottom" on-tap="{{toggleDialog}}"></paper-button>
    <paper-button label="Center" on-tap="{{toggleDialog}}"></paper-button>
    <!-- ... -->
    <core-style id="theme">
      paper-button {
        border: 5px solid {{g.primaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="theme"></core-style>
  </template>
  <script type="application/dart" src="lorem_dialog.dart"></script>
</polymer-element>
Try as I might, I cannot get this to work. I wind up with an error about the g property not being defined:
Exception: Error evaluating expression 'CoreStyle.g.primaryColor': Class 'LoremDialog' has no instance getter 'CoreStyle'.

NoSuchMethodError: method not found: 'CoreStyle'
Receiver: Instance of 'LoremDialog'
Arguments: []
A hint from James Hurford suggests that including the script that sets the global color on the same page that defines the styles will work:
  <body>
    <h1>Play Paper</h1>

    <lorem-dialog></lorem-dialog>

    <core-style id="legacy-button">
      :host paper-button {
        border: 5px solid {{g.primaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <script>
      CoreStyle.g.primaryColor = 'orange';
    </script>
  </body>
In fact, that does work. My Polymer element can then reference the "legacy-button" core style and the buttons will have orange borders. But that does not seem right to me. I feel like I ought to set the global property in the main page and use it inside Polymer elements. I should not have to build the entire style in the containing page—the containing page then needs to know too much about the internals of my Polymer element. That defeats the purpose of encapsulating all of this stuff inside Polymer elements, does it not?

To decide which I right, I try it in JavaScript as well. And find… the same thing. In JavaScript, I do not get the exceptions that I do from Dart, but I still do not see global core styles inside Polymer element <core-style> elements:
<link rel="import"
      href="/bower_components/polymer/polymer.html">
<!-- ... -->
<link rel="import"
      href="/bower_components/core-style/core-style.html">
<polymer-element name="lorem-dialog">
  <template>
    <paper-button label="Bottom" on-tap="{{toggleDialog}}"></paper-button>
    <paper-button label="Center" on-tap="{{toggleDialog}}"></paper-button>
    <!-- ... -->
    <core-style id="theme">
      :host paper-button {
        border: 5px solid {{g.primaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="theme"></core-style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
With this, I get 5px solid button borders, but the color is empty so the buttons default to black:



What I can do in the JavaScript version of Polymer that did not work in Polymer.dart is make a local copy of the g property in my sample Polymer class:
<link rel="import"
      href="/bower_components/polymer/polymer.html">
<!-- ... -->
<link rel="import"
      href="/bower_components/core-style/core-style.html">
<polymer-element name="lorem-dialog">
  <template><!-- ... --> </template>
  <script>
    Polymer('lorem-dialog', {
      created: function() {
        this.g = CoreStyle.g;
      },
      // ...
    });
  </script>
</polymer-element>
This makes the g property available inside the <template> of my Polymer element:



So am I wrong in my assumption that I should be able to set a global CoreStyle property in the containing page and use it inside elements? It sure seems that way. I have a workaround, but a workaround implies that I am working against the library, not using it as intended. So the question remains: am I doing something wrong or is core-style not meant to work like this?


Day #161


Friday, August 22, 2014

Getting Started with Core-Style for Theming Polymer


I have enjoyed investigating Material Design. It provided much needed inspiration and new ideas on design—especially with regards to the use of motion in design. I remain concerned on how or if to use things like Paper Elements in “legacy” applications.

Paper elements are a Material take on custom Polymer elements. They look pretty, but they wind up looking—for lack of a better word—Googly:



Heck, even if I am following Material design without Paper elements, I find myself building applications that might look quite at home in a Google property:



In many cases, this would be a good thing. Let's face it, if I am designing something, it would always be a good thing. But there are times that a client wants to continue using their own styles in Polymer elements. There are even times that the people writing the checks cannot be convinced to stray at all from something like Bootstrap.

So what to do in those cases? Is a polymer element designed with Material principles impossible to use in legacy designs without looking impossibly out of place? To a certain extent, this is why I built <apply-author-styles>—to pull styles from a containing pages into a Polymer element.

That is helpful, but not really a theming solution. For theming, the Polymer team introduced core-style. Let's say that a client insists on a large orange border with initial caps and the lovely Arial font. From last night, paper buttons look like:



To get my legacy style in there, I can import core-style using the usual Polymer.dart:
  <head>
    <!-- Load platform polyfills -->
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>

    <!-- Load component(s) -->
    <link rel="import"
          href="packages/paper_elements/paper_dialog_transition.html">
    <link rel="import"
          href="packages/paper_elements/paper_dialog.html">
    <link rel="import"
          href="packages/paper_elements/paper_button.html">
    <link rel="import"
          href="packages/core_elements/core_style.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
Actually using core-style is a two step process. First I need to define the styles that I want to use:
    <core-style id="legacy-button">
      paper-button {
        text-transform: initial;
        font-family: Arial, sans-serif;
        border: 5px orange solid;
        padding: 2px 5px;
      }
    </core-style>
That should keep my clients happy. Now I need to actually apply it. In core-style parlance, I need to reference it:
    <core-style ref="legacy-button"></core-style>
With that, get:



This is a simple example. For actual theming, I might want to expose style variables that can be set. Core-style supports setting these in the global style group with CoreStyle.g. If I wanted to style my example <lorem-dialog> element, I might do so with:
    <lorem-dialog></lorem-dialog>
    <script>
      CoreStyle.g.fontFamily = 'Arial, sans-serif';
      CoreStyle.g.buttonBorder = '5px orange solid';
    </script>
I could then apply these inside my element with something along the lines of:
<link rel="import"
      href="../../../packages/polymer/polymer.html">
<link rel="import"
      href="../../../packages/core_elements/core_style.html">
<!-- ... -->
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <core-style id="legacy-button">
      paper-button {
        text-transform: initial;
        font-family: {{g.fontFamily}};
        border: {{g.buttonBorder}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="legacy-button"></core-style>
  </template>
  <script type="application/dart" src="lorem_dialog.dart"></script>
</polymer-element>
Only I am unable to make this work in Polymer.dart. Try as I might, the g property is not available:
Exception: Error evaluating expression 'g.buttonBorder': Class 'LoremDialog' has no instance getter 'g'.

NoSuchMethodError: method not found: 'g'
Receiver: Instance of 'LoremDialog'
Arguments: []
Darn it.

I think I am doing this right, but I am unable to discern a way to tease the g property into my Polymer.dart element. I will likely give this a try in JavaScript tomorrow to make sure that I am not overlooking something obvious.


Day #160

Thursday, August 21, 2014

Paper-Dialog-Transition (Polymer.Dart)


I love me transitions and animations, so I will take one more day to explore Paper Elements in Polymer.dart. Paper are the Polymer spin on Google's Material Design. Although I still worry that Material tends to look too Googly, I cannot argue with the concepts and emphasis that Material has for design. So I keep playing and learning.

Last night, I created a fairly simple paper-dialog in Polymer.dart:



What is most remarkable to me is that it worked so easily. A while back, I could barely get Core Elements to work in Polymer.dart. Now both Core and Paper work with ease. The team behind Polymer.dart has been active (thanks folks!).

One of the core tenants of Material Design is “Motion with meaning.” There is no motion in my paper-dialog and I can't help but notice that there is a paper-dialog-transition element. As if that were not enough to tempt me to investigate, the documentation on the JavaScript version is currently empty. So really, I can't help but investigate.

My first thought is to use the <paper-dialog-transition> as a drop-in replacement for last night's <paper-dialog>:
    <paper-dialog-transition heading="Title for dialog" opened="true">
      <p>Lorem ipsum ....</p>
      <p>Id qui scripta ...</p>
      <paper-button label="More Info..." dismissive></paper-button>
      <paper-button label="Decline" affirmative></paper-button>
      <paper-button label="Accept" affirmative autofocus></paper-button>
    </paper-dialog-transition>
I also add the appropriate <link> import of paper-dialog-transition to the document <head>:
  <head>
    <!-- Load platform polyfills -->
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>

    <!-- Load component(s) -->
    <link rel="import"
          href="packages/paper_elements/paper_dialog_transition.html">
    <link rel="import"
          href="packages/paper_elements/paper_dialog.html">
    <link rel="import"
          href="packages/paper_elements/paper_button.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
But that does not work. Loading the page, I do not find a pre-opened dialog. I find nothing. If I query in Chrome's JavaScript console for paper-dialog-transition elements, I find not one, but three:



The first two are added by the Polymer transformer in Dart Pub. The last is the real one that I added. And it does not behave like a <polymer-dialog>:
> d = document.querySelectorAll('paper-dialog-transition')[2]
    <paper-dialog-transition heading=​"Title for dialog" opened=​"true" hidden>​…​</paper-dialog-transition>​
> d.toggle()
    TypeError: undefined is not a function
It seems like <paper-dialog-transition> is adding those first two copies of itself for a reason. I am ultimately unable to figure out why by reading the code. Or rather, I stumble across some demo code that seems to do use the element.

The key seems to be using the IDs of the extra <paper-dialog-transition> elements in transition attributes of <paper-dialog> elements:
    <paper-dialog heading="Bottom Dialog" transition="paper-dialog-transition-bottom">
      <p>Lorem ipsum ....</p>
      <p>Id qui scripta ...</p>
      <paper-button label="More Info..." dismissive></paper-button>
      <paper-button label="Decline" affirmative></paper-button>
      <paper-button label="Accept" affirmative autofocus></paper-button>
    </paper-dialog>
I add a similar <paper-dialog> with a transition of paper-dialog-transition-center.

The final piece of the puzzle comes directly from the demo page. I add two buttons that invoked the toggle() method on the appropriate dialog:
    <paper-button label="Bottom" onclick="toggleDialog('paper-dialog-transition-bottom')"></paper-button>
    <paper-button label="Center" onclick="toggleDialog('paper-dialog-transition-center')"></paper-button>
  <script>
    function toggleDialog(transition) {
      var dialog = document.
        querySelector('paper-dialog[transition=' + transition + ']');
      dialog.toggle();
    }
  </script>
The result is quite pleasing pair of dialog transitions. One travels up from the bottom of the page, the other expands from the center:

video

The <paper-dialog-transition> transitions work nicely with the <paper-dialog> elements. Toggling the dialog, clicking elsewhere, clicking decline, even hitting escape all result in the transition reversing itself. All I needed to make this work was the addition of transition attributes and the import of paper_dialog_transition.html (removing this import disables the transitions).

I have to admit that the usage of <paper-dialog-transition> was not obvious to me. By extension, I have the feeling that I am a little weak on core-transition-css (which paper-dialog-transition extends). This may be worth a little more exploration. Tomorrow.

Day #159

Wednesday, August 20, 2014

Paper Dialogs with Polymer.dart


I have quite enjoyed playing with Material Design concepts of late. Interestingly, the solutions that I wound up with, both in Dart and JavaScript, rely on Core Elements instead of the material design based Paper Elements.

Rather than try to cram Paper into my very pretty Core/Material design in <x-pizza>, I start a completely new project. In this case, I am going to try to use the Paper elements directly in a Dart application rather than pulling them into another Polymer.dart element.

I start this “paper” application with a pubspec.yaml of:
name: paper
dependencies:
  polymer: any
  paper_elements: any
transformers:
- polymer
I had originally thought to skip the polymer dependency, but without it, pub serve will complain:
$ pub serve
Error in pubspec for package "paper" loaded from pubspec.yaml:
"transformers.polymer" refers to a package that's not a dependency
And if I try to remove the transformer, Dartium complains:
Exception: Missing initialization of polymer elements. Please check that the list of entry points in your pubspec.yaml is correct. If you are using pub-serve, you may need to restart it.
So, even though I am not creating my own Polymer elements, I need Polymer listed as a dependency. I also have to initialize the page in the same manner—the index.html file looks like:
<!DOCTYPE html>
<html>
  <head>
    <title>Play Paper</title>

    <!-- Load platform polyfills -->
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>

    <!-- Load component(s) -->
    <link rel="import"
          href="packages/paper_elements/paper_dialog.html">
    <link rel="import"
          href="packages/paper_elements/paper_button.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>

  </head>
  <body>
    <h1>Play Paper</h1>
    <!-- ... -->
  </body>
</html>
That is the same setup that I use with any Polymer code—the same way to load the JavaScript polyfills (and associated Dart support), the same way to import the elements (Paper in this case) and the same way to start the Polymer.dart system.

As the elements being imported suggest, I am going to try a simple Paper dialog in Dart, so I add the following custom HTML (copied from the JavaScript documentation):
    <paper-dialog heading="Title for dialog" opened="true">
      <p>Lorem ipsum ....</p>
      <p>Id qui scripta ...</p>
      <paper-button label="More Info..." dismissive></paper-button>
      <paper-button label="Decline" affirmative></paper-button>
      <paper-button label="Accept" affirmative autofocus></paper-button>
    </paper-dialog>
The trick with this simple, open-by-default dialog is the opened attribute on <paper-dialog>. This would normally be accomplished via the toggle() method on <paper-dialog> elements, but I am simply hoping to verify that Paper works tonight. I will worry about additional interaction another night.

After starting the pub serve test web server, I find:



That was easy! But it certainly could look better—especially when I compare it to everything else in Material design. To get somewhat better results, I follow along with the Material Typography guide, and switch to the Roboto font:
  <head>
    <title>Play Paper</title>
    <link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
    <style>
      html { font-family: 'Roboto', sans-serif; }

      paper-button[autofocus] {
        color: #4285f4;
      }

      paper-button:hover {
        color: #2a56c6;
      }

      paper-button::shadow #ripple {
        color: #2a56c6;
      }
    </style>

    <!-- Load platform polyfills -->
    <!-- ... -->
  </head>
This winds up looking very Googly (and much better):



So Paper in Dart turns out to be blessedly easy—at least getting started with it. I may add some interaction or play with some additional elements tomorrow. Or, if that turns out to be too easy (because Polymer.dart works too darn well), then I may move on to other topics.


Day #158

Tuesday, August 19, 2014

Core-Icon in Polymer.dart


With little difficulty, I was able to convert vanilla HTML in my <x-pizza> Polymer.dart element to Core Elements / Material Design:

video

The conversion was made easy thanks to earlier work on a JavaScript version of <x-pizza> and the similarities between Polymer.dart and Polymer. More specifically, the Dart version of Core Elements uses the JavaScript Core Elements making things even easier the more I stick with core elements.

Except for <core-icon>. For some reason, my custom SVG images for pizza toppings are all showing up quite small:



In the JavaScript version, from which the Dart custom HTML and CSS was copied directly, the SVG icons look like:



In both Dart and JavaScript versions of the elements, I am using core-icon exactly the same:
              <div class="chip-icon">
                <core-icon
                   hero-id="first_half-icon"
                   hero?="{{selectedTopping == 'first_half' }}"
                   src="packages/paper/assets/first_half.svg"></core-icon>
              </div>
Furthermore, I am styling them exactly the same (following the core-icon documentation):
      .chip-icon core-icon {
        color: white;
        height: 100px;
        width: 100px;
      }
Digging a bit deeper, I find that the Dart version's DOM does not contain a <div> child element like the JavaScript version. Also, the Dart version seems to be explicitly setting the width and heigh to 24 pixels:



Not surprisingly given the differences in results, the Dart Core Element implementation of core-icon turns out to be different than the JavaScript. Instead of re-using the JavaScript core-icon, it has its own pure Dart implementation that includes a size attribute:
    /**
     * Specifies the size of the icon in pixel units.
     *
     * @attribute size
     * @type string
     * @default 24
     */
    size: 24,
It may not be identified in the core_elements package documentation as being pure Dart, but I can always forgive an omission when the code is documented.

After adding the seemingly required size attribute:
              <div class="chip-icon">
                <core-icon
                   hero-id="first_half-icon"
                   hero?="{{selectedTopping == 'first_half' }}"
                   size="100"
                   src="packages/paper/assets/first_half.svg"></core-icon>
              </div>
I have my <x-pizza> element working perfectly under a Material design.

video

I have this working with Material design principles and with Core elements, but I have yet to try Paper elements in Dart. I have no reason to suspect that they will give me any trouble, but it is still worth playing with a little. Tomorrow.

Day #157

Monday, August 18, 2014

Core Elements in Polymer.Dart (Including Animation)


When last I tried to use Core Element in Polymer.dart, it did not go well. I know the team has made incredible progress in the interim, so tonight seems an opportune time to find out how much better things are.

The Core and Paper element work that I have been building in the JavaScript <x-pizza> element should be a solid test. I start, as I do with all Dart projects, with a pubspec.yaml including Paper and Core dependencies:
name: paper
dependencies:
  polymer: any
  core_elements: any
  paper_elements: any
dev_dependencies:
  scheduled_test: any
transformers:
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
A quick pub install and I should be good to go:
$ pub install
Resolving dependencies... (1.4s)
Downloading paper_elements 0.1.0+3...
Downloading core_elements 0.1.1+2...
Downloading quiver 0.18.2...
Got dependencies!
I add some initial core imports and elements to the <x-pizza-toppings> child element:
<link rel="import" href="../../../packages/polymer/polymer.html">
<link rel="import" href="../../../packages/core_elements/core_icons.html">
<link rel="import" href="../../../packages/core_elements/core_item.html">
<polymer-element name="x-pizza-toppings">
  <template>
    <!-- ... -->
    <template repeat="{{ingredient in ingredients}}">
      <core-item
         icon="add-circle"
         label="{{ingredient}}"
         on-tap="{{add}}">
      </core-item>
    </template>
    <!-- ... -->
  </template>
  <script type="application/dart" src="x_pizza_toppings.dart"></script>
</polymer-element>
And, when I load things up in Dartium, I get a nice old failure:
Exception: Unsupported operation: Couldn't find `document._registerDartTypeUpgrader`. Please make sure that `packages/web_components/dart_support.js` is loaded and available before calling this function.
Aha! So that is what the dart_support.js script does. I know that it is included in generated Polymer.dart code, but until now I could find no use for it.

To resolve, I add this back in the main index.html page that pulls in Polymer, the Polymer elements that I want to use, and initializes everything:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Load platform polyfills -->
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>

    <!-- Load component(s) -->
    <link rel="import"
          href="packages/paper/elements/x-pizza.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body>
    <div class="container">
      <h1>Ye Olde Dart Pizza Shoppe</h1>
      <x-pizza></x-pizza>
    </div>
  </body>
</html>
That aside, there are relatively few issues that I have to address converting the <x-pizza> element from JavaScript to Dart. I have to mark properties that are used in the template as @observable. I believe that was the only “significant” change required and it was not that difficult.

I do note that I can omit the import of transitions (i.e. the hero transition code). When I was building the JavaScript version, I thought this was required, but even after double checking the JavaScript version, it seems that importing the core-animated-pages is sufficient to pull in the transition code, which makes sense:
<link rel="import" href="../../../packages/polymer/polymer.html">
<link rel="import" href="../../../packages/core_elements/core_icon.html">
<link rel="import" href="../../../packages/core_elements/core_pages.html">
<link rel="import" href="../../../packages/core_elements/core_animated_pages.html">
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <template>
    <!-- ... -->
    <core-animated-pages selected="{{toppingsListOrDetails}}" transitions="hero-transition">
      <!-- ... -->
    </core-animated-pages>
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
And all of this works just fine:

video

Well, maybe 100% fine. I need to figure out why the icons are the wrong size. There is also a flash of unstyled CSS when the page first loads. I think this would not be an issue if the element were compiled to JavaScript (instead of loaded via pub serve)—something to check another day. Those minor issues aside, this worked pretty well. Infinitely better than the last time I tried it!


Day #156