Wednesday, August 20, 2014

Making Some Progress

I just started on a new project recently and one of the things on the page I was working on was displaying progress bars in different colors.  The progress bars need to display an indeterminate state and after talking with a co-worker we thought making it look like KITT from Night Rider was the best (and most awesome) way to display that.


Is that you Mr. Feeny?
Because of a lot of my work being done on the canvas, and to have communication being done with web sockets, I am only supporting modern browsers for this app.  I knew there are a ton of new elements in HTML 5 and one of my personal goals is to become better at writing semantically correct html.  A quick look through the elements, and I found the <progress> tag.  Even better, it comes with an indeterminate state and is supported in the browsers I care about.  My next requirement was just to style the progress elements different colors based on their states.  I thought this is going to be easy.  All I need to do is add some classes to set the color and I'm done!


So after a quick search I find out you pretty much can't do anything to style a progress element.  Which made me think...  how do you expect people to use this element if they can't make it look how they need it to?

I was determined to use CSS only to get this to work and make my page semantically awesome.  I found some sites like this that show you how to clear out the native browser appearance and add your own.  So I started with that and then added my own classes to style them how I needed.  A lot of CSS later, I came up with the following:

.progressContainer{
    position:relative;
    width: 100px;
}

progress{
  height:5px;
  border:0;
  appearance: none;
  -moz-appearance: none;
  -webkit-appearance: none;
  width:100% !important;
 
}

progress.connected::-webkit-progress-value{
  background: green;
} 
progress.connected::-moz-progress-value{
  background: green;
}
progress.connected::progress-value{
  background: green;
}
progress.connected::-webkit-progress-bar{
  background: green;
} 
progress.connected::-moz-progress-bar{
  background: green;
}
progress.connected::progress-bar{
  background: green;
}
progress.connected:not([value])::-webkit-progress-bar{
  background:green;
}
progress.connected:not([value])::-moz-progress-bar{
  background:green;
}
progress.connected:not([value])::progress-bar{
  background:green;
}

With the lower half repeated for each class that I wanted, I was almost done. While verbose, I was able to style the progress as necessary. The only thing left was to get KITT to work. After some quick thinking I figured I would use a ::before pseudo element and animate it.
@-webkit-keyframes knightRider {
   0% {
 left: 0%;
      }
 
  50% {
 left:100%;
        margin-left:-10px;
      }
  100%{
 left:0%;
      }
}
@keyframes knightRider {
   0% {
 left: 0%;
      }
 
  50% {
   left:100%;
        margin-left:-10px;
      }
  100%{
 left:0%;
      }
}

progress.searching::before{
  content:"";
  background-color:blue;
  width:10px;
  height:5px;
  position: absolute;
  animation: knightRider 2s infinite linear;
  -webkit-animation: knightRider 2s infinite linear;
}
A quick refresh of my fiddle and BOOM it was working.  Now I just need to make sure it is working in Firefox which should be just a formality, right?  Wrong!  Only webkit browsers support ::before and ::after on progress for some bizzaro reason.  So I ended up adding a hidden div next to the progress bar that would use the animation from above and only show up when a specific class is added to the progress bar.

See the Pen Progress by Andy (@andyjmeyers) on CodePen.




Finally some progress...

Friday, August 8, 2014

Beyond The Wall (Proxy)

North of the Wall, things are different. That's where the children went, and the giants, and the other old races.
Nowadays a lot of development resources are managed via a package manager. While this makes life a lot easier almost all of the time, it can be a little bit of a headache if you aren't set up like everyone else is. When I first started out at Motorola, I had a rough time getting some of these to work. I finally realized that it had to do with me being behind the company proxy.  So here is what you need to do to get a couple things to work if you are behind a company proxy so hopefully someone else doesn't have to go through what I did.

1. Bower
Open up your bower config file (.bowerrc) and add the following lines:

  "proxy":"http(s)://server:port/",
  "https-proxy":"http(s)://server:port/"

2. Git 

Open up your .gitconfig file and add the following lines:

[http]
    proxy = http(s)://server:port/
[https]
    proxy = http(s)://server:port/

3. NPM

I believe these get stored in the .npmrc file, but npm recommends you set them via command line with these commands.

npm config set proxy http(s)://server:port npm config set https-proxy http(s)://server:port

Your .npmrc file then looks like this:

proxy =http(s)://server:port
https_proxy = http(s)://server:port


*The image above is copyrighted by HBO

Saturday, August 2, 2014

Testing Angular Directives That Repeat With Transclude

The title is a bit of a mouthful, but recently I was writing a unit test for an angular directive I was working on.  The directive's purpose is to flatten a binary tree and display all of the leafs in a list.  A demo of the working code is here: JS Fiddle.

Here is the actual directive:
.directive('flattenTree', function () {

    return {
        restrict: 'E',
        template: "<li ng-repeat='node in tree' ng-transclude></li>",
        transclude: true,
        scope: {
            root: '='
        },
        link: function (scope) {
            scope.tree = [];
            var index = 0;
            var queue = [];
            queue.push(scope.root);
            while (queue.length != 0) {
                var node = queue.shift();
                if (node['left']) {
                    queue.unshift( node.right, node.left);
                } else {
                    scope.tree.push(node);
                }
            }
  
        }
    };

});

When writing the tests I wanted to validate a few things.

1. If the root node is null then it doesn't display anything
2. That the root node is traversed correctly and all leaf nodes are displayed in order
3. That each node has a corresponding <li> tag and the html in the transclude is displayed for each one.

Here is what I came up for my test that is written with Jasmine.

My setup code in which I grab the angular objects that I need to run the tests.
var $compile,
    $rootScope;

// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){

  // The injector unwraps the underscores (_) from around the parameter names when matching
  $compile = _$compile_;
  $rootScope = _$rootScope_;

}));


And my 3 tests.  Each test consist of 3 parts.

1. Setting up the data and the template I want to use (always using <flatten-tree> since that is my directive).
2. Compiling the template and calling $digest to make the angular magic work.
3. Validating the result & DOM with the jqlite that comes with angular.

it('replaces the element with null when empty', function() {
    // Compile a piece of HTML containing the directive
    var template = "<flatten-tree><span>{{node}}</span></flatten-tree>";
    var element = $compile(template)($rootScope);

    // fire all the watches, so the scope expression will be evaluated
    $rootScope.$digest();

    // Check that the compiled element contains the templated content
    expect(element.children().length).toBe(0);
});

it('replaces the element with a single li when one node', function() {

     $rootScope.node = "foobar";
    var template = "<ul><flatten-tree root='node'><span>{{node}}</span></flatten-tree></ul>";
    // Compile a piece of HTML containing the directive
    var element = $compile(template)($rootScope);
    // fire all the watches, so the scope expression will be evaluated
    $rootScope.$digest();

    // Check that the compiled element contains the templated content
    expect(element.find("li").length).toBe(1);
    expect(element.find("span").length).toBe(1);
    expect(element.find("span").text()).toBe("foobar");
});

it('traverses the tree and replaces the element with a list', function() {

    $rootScope.node = {
        right:{
            right:1,
            left:2
        },
        left:{
            right:{
                right:3,
                left:4
            },
            left:{
                right:5,
                left:6
            }
        }
    };

    var template = "<ul><flatten-tree root='node'><span>{{node}}</span></flatten-tree></ul>";

    // Compile a piece of HTML containing the directive
    var element = $compile(template)($rootScope);

    // fire all the watches, so the scope expression will be evaluated
    $rootScope.$digest();

    // Check that the compiled element contains the templated content
    expect(element.find("li").length).toBe(6);
    expect(element.find("span").length).toBe(6);

    var spanElements = element.find("span");
    for (var i = 0; i < spanElements.length; i++) {
           expect(spanElements[i].innerText).toBe((i+1).toString());
    }
});


Easy as pie!