Developing on Staxmanade

TVJS TVHelpers DirectionalNavigation and Adapting/Hacking some WinJS Focus Management

(Comments)

So, Microsoft created what really turned out to be an amazing set of HTML/JS/CSS controls when they released the WinJS library. Not to go too much into the history, but honestly I hated it when I first had to use it. But, let me clarify. It wasn't until this last year when I learned that I didn't hate the WinJS controls by themselves, but I despised the way you declared their usage using the specialized win-* html attributes. It felt like a total hack to get an app up and running by littering semantic html with these attributes.

Then along comes a little toy project they created called react-winjs and all of a sudden the WinJS "Components" made total sense. When looking at them through the lense of WinJS through ReactJS components was the first time that I not only clicked with WinJS, but I actually fell in lov... (well I won't go that far), but was excited enough about them to pick them as the primary U.I. control suite while building out a little side-project.

Fast forward a year of development, and Microsoft essentially bailed on WinJS but at least they left it out in the open so I could hack on it and continue to depend on my own fork for the time being.

Then, they announce a NEW & SHINY library that can be used to help develop UWP and TV/Xbox One apps which is great. Except, WinJS doesn't work with this new library out-of-the-box, and since Microsoft isn't adding new features to WinJS, they likely never will build-in compatibility with the new & shiny library.

Guess that means we (I) have to figure it out on my own. And although I write this knowing that I'm probably the ONLY developer on the planet using this combination of libraries, I wanted to put out some of the hacks/code I've thrown together to get some WinJS controls to play nice with TVJS with regards to focus management.

What is focus management you say?

In the context of an Xbox app, the idea is to take your'e web-page-app and get rid of the ugly mouse-like-cursor you'd see if you didn't do this and replace it with a controller navigable approach - so up/down/left/right on the controller moves the visible "focus" around the application and the A button "presses enter" (or invokes) the control/button/etc.

What IS provided by TVJS

The TVJS library has a helper within it called DirectionalNavigation and is great in that it provides a focused and specific API to enable focus management while developing a Xbox App UWP Javascript (& C#) apps.

Just dropping the library in is enough to get much of the basics to work with most web apps.

However, the conflict between this and WinJS comes into play because WinJS also tried to implement some of their own focus management and the mix of these two just doesn't quite cut it.

Get rid of mouse cursor

Well, this isn't really a hack:

If you're looking at building a UWP JavaScript app for the Xbox, and tried to run your app on the Xbox (in dev mode), you may have noticed that your app behaves almost like it was just another web-page and doesn't default the cursor focus the way other xbox apps work. You're app just has a mouse-like cursor.

The way to deal with this is just by accessing the browser's gamepad api. Now, the Microsoft TVJS TVHelpers DirectionalNavigation library automatically does this for you, but for a better experience if you don't want to wait for the browser to download this library, you can manually access the api to hide the mouse cursor by throwing this at the top of you're start page EX: index.html

    <script>
        // Hide the Xbox/Edge mouse cursor during load.
        try {
            navigator.getGamepads();
        } catch(err) {
            console && console.error('Error with navigator.getGamepads()', err);
        }
    </script>

Just by calling navigator.getGamepads(), this tells the browser/hosted web app that you are going to take control of the app's focus management and to hide the mouse cursor.

Once you've done this and you're app loads up with the TVJS DirectionalNavigation library and in my case some WinJS controls, focus management mostly works (sort-of).

Completely Remove XYFocus built-in to WinJS:

This is about as ugly as they get...

The below code is bascially looking for the XYFocus handlers that WinJS is trying add to the document and we wan to not allow it to get added.

This XYFocus handler really creates havoc when we add the XYFocus handler from TVSJ DirectionalNavigation.

// HacktyHackHack
(function() {
  var realAddEventListener = document.addEventListener;
  document.addEventListener = function(eventName, handler, c){
    if (handler.toString().indexOf('function _handleKeyEvent(e)') >= 0) {
      console.warn("Ignoring _handleKeyEvent...", eventName, handler, c);
      return;
    }
    if (handler.toString().indexOf('function _handleCaptureKeyEvent(e)') >= 0) {
      console.warn("Ignoring _handleCaptureKeyEvent...", eventName, handler, c);
      return;
    }
    return realAddEventListener(eventName, handler, c);
  };
}());

By not allowing WinJS to add it's XYFocus handlers, we can avoid many of the issues that I worked through below...

Dealing with a WinJS Pivot control

For my app, the first control I ran into trouble with was the WinJS Pivot control. This control already does some focus management all by itself, and it's own management style contradicts the way the DirectionalNavigation helper works. So we basically have to detect focus on it, turn of TVJS focus management and handle it internally (until we leave focus of the Pivot).

To work through that, I created the following helper function:


WinJS.UI.Pivot.prototype._headersKeyDown = function (e) {
    if (this.locked) {
        return;
    }
    if (e.keyCode === Keys.leftArrow ||
        e.keyCode === Keys.pageUp ||
        e.keyCode === Keys.GamepadDPadLeft ||
        e.keyCode === Keys.GamepadLeftThumbstickLeft) {
        this._rtl ? this._goNext() : this._goPrevious();
        e.preventDefault();
    } else if (e.keyCode === Keys.rightArrow ||
               e.keyCode === Keys.pageDown ||
               e.keyCode === Keys.GamepadDPadRight ||
               e.keyCode === Keys.GamepadLeftThumbstickRight) {
        this._rtl ? this._goPrevious() : this._goNext();
        e.preventDefault();
    }
};

function handlePivotNavigation(pivotElement) {
  console.log("handlePivotNavigation", pivotElement);
  if (!pivotElement) {
    throw new Error("handlePivotNavigation cannot use pivotElement as it wasn't passed in");
  }

  var pivotHeader = pivotElement.querySelector('.win-pivot-headers')

  if (!pivotHeader) {
    let msg = "handlePivotNavigation cannot find .win-pivot-headers in";
    console.error(msg, pivotElement);
    throw new Error(msg);
  }


  pivotHeader.addEventListener('focus', function() {
    console.log("pivotHeader focus");
    DirectionalNavigation.enabled = false;
  });
  pivotHeader.addEventListener('keyup', function(eventInfo) {
    console.log('pivot keyup ', eventInfo.keyCode, eventInfo.key);

    switch(eventInfo.keyCode) {
      case 204: // gampead down
      case 40: // keyboard down
        DirectionalNavigation.enabled = true;
        var target = DirectionalNavigation.findNextFocusElement('down');
        if (target) {
          target.focus();
          eventInfo.preventDefault();
        }
        break;
      case 203: // gamepad up
        // since the Pivot is at the top of the page - we won't release
        // control, or try to navigate up??? (maybe consider flowing up from the bottom of the page?)
        break;
      // case 205: // gamepad left arrow
      // case 211: // gamepad 211 GamepadLeftThumbstickUp
      // case 200: // gamepad left bumper
      //   pivotElement.winControl._goPrevious();
      //   eventInfo.preventDefault();
      //   break;
      // case 206: // gamepad right arrow
      // case 213: // gamepad 213 GamepadLeftThumbstickRight
      // case 199: // gamepad 199 GamepadRightShoulder
      //   pivotElement.winControl._goNext();
      //   eventInfo.preventDefault();
      //   break;
    }
  });
}

And use it by doing the following in my React page:

    componentDidMount() {
        var pivot = ReactDOM.findDOMNode(this.refs.pivot);
        handlePivotNavigation(pivot);
    }

Or if you're not using React you can likely just go:

    var pivot = document.getElementById('my-pivot-id');
    handlePivotNavigation(pivot);

It's not pretty, but has been working for me so far.

Now when I navigate around using an Xbox controller I can properly navigate around the WinJS Pivot.

Next up are ItemContainers.

UPDATE:

With the added (remove XYFocus above - I removed the below hack)

This one is a total hack, and I look forward to a better solution, but for now it's been working.

The issue I was seeing was with WinJS ItemContainers and the TVJS library applying a separate forced "click" on the element when the control itself has already "clicked/invoked" the element.

The real fix would likely to figure out how to get the ItemContainer to event.preventDefault() and/or event.stopPropagation() and avoid the bubbling up to the document keyup event handler that DirectionalNavigation has under it's control, but WinJS ItemControl management is just so complicated that this hack was easier to figure out at the time I threw it together.

So what does this do?

It's basically hijacking the DirectionalNavigation._handleKeyUpEvent function, and re-writing it with one that ignores the keyup event if the currently focused element is an ItemContainer.

// Hack to avoid Item containers getting double click
var originalDNKeyUp = TVJS.DirectionalNavigation._handleKeyUpEvent
TVJS.DirectionalNavigation._handleKeyUpEvent = function (e) {
console.log("Check for itemContaner", event.target.className)
  if (e.target.className.split(" ").indexOf("win-itemcontainer") >= 0) {
    console.log("MonkeyHack on DirectionalNavigation - SKIPPING CLICK");
    return;
  }
  return originalDNKeyUp.apply(null, arguments);
}
document.removeEventListener("keyup", originalDNKeyUp);
document.addEventListener("keyup", TVJS.DirectionalNavigation._handleKeyUpEvent);

It's not pretty, but meh, is working so far.

ItemContainers within a ContentDialog

UPDATE

I gave up on ContentDialog, and just started using react-modal

That's just a big mess from what I could figure out. I was able to get it working by using the ContentDialog but manually creat my own buttons as the ItemContainer in combination with the dialog kept swallowing events that didn't allow focus navigation to be sucessful. The internals of what was holding me back didn't appear to be monkey-patch-able from what I could tell... ugh...

Next up is a ListView hack,

This one is a hack proposed by Todd over on the GitHub issues.

I've essentially taken the original implementation of WinJS.UI.ListView.prototype._onFocusIn, and if you look for the line starting with /* JJ */ below you can see the change there.

Don't know what this actually could mean from other scenarios, but for now it's allowing the ListView to focus properly on my initial xbox testing.

var _Constants = WinJS.UI;
var _UI = WinJS.UI;

WinJS.UI.ListView.prototype._onFocusIn = function ListView_onFocusIn(event) {
                    this._hasKeyboardFocus = true;
                    var that = this;
                    function moveFocusToItem(keyboardFocused) {
                        that._changeFocus(that._selection._getFocused(), true, false, false, keyboardFocused);
                    }
                    // The keyboardEventsHelper object can get focus through three ways: We give it focus explicitly, in which case _shouldHaveFocus will be true,
                    // or the item that should be focused isn't in the viewport, so keyboard focus could only go to our helper. The third way happens when
                    // focus was already on the keyboard helper and someone alt tabbed away from and eventually back to the app. In the second case, we want to navigate
                    // back to the focused item via changeFocus(). In the third case, we don't want to move focus to a real item. We differentiate between cases two and three
                    // by checking if the flag _keyboardFocusInbound is true. It'll be set to true when the tab manager notifies us about the user pressing tab
                    // to move focus into the listview.
                    if (event.target === this._keyboardEventsHelper) {
                        if (!this._keyboardEventsHelper._shouldHaveFocus && this._keyboardFocusInbound) {
                            moveFocusToItem(true);
                        } else {
                            this._keyboardEventsHelper._shouldHaveFocus = false;
                        }
                    } else if (event.target === this._element) {
                        // If someone explicitly calls .focus() on the listview element, we need to route focus to the item that should be focused
                        moveFocusToItem();
                    } else {
                        if (this._mode.inboundFocusHandled) {
                            this._mode.inboundFocusHandled = false;
                            return;
                        }

                        // In the event that .focus() is explicitly called on an element, we need to figure out what item got focus and set our state appropriately.
                        var items = this._view.items,
                            entity = {},
                            element = this._getHeaderOrFooterFromElement(event.target),
                            winItem = null;
                        if (element) {
                            entity.index = 0;
                            entity.type = (element === this._header ? _UI.ObjectType.header : _UI.ObjectType.footer);
                            this._lastFocusedElementInGroupTrack = entity;
                        } else {
                            element = this._groups.headerFrom(event.target);
                            if (element) {
                                entity.type = _UI.ObjectType.groupHeader;
                                entity.index = this._groups.index(element);
                                this._lastFocusedElementInGroupTrack = entity;
                            } else {
                                entity.index = items.index(event.target);
                                entity.type = _UI.ObjectType.item;
                                element = items.itemBoxAt(entity.index);
                                winItem = items.itemAt(entity.index);
                            }
                        }

                        // In the old layouts, index will be -1 if a group header got focus
                        if (entity.index !== _Constants._INVALID_INDEX) {
/* JJ */                            /*if (this._keyboardFocusInbound || this._selection._keyboardFocused())*/ {
                                if ((entity.type === _UI.ObjectType.groupHeader && event.target === element) ||
                                        (entity.type === _UI.ObjectType.item && event.target.parentNode === element)) {
                                    // For items we check the parentNode because the srcElement is win-item and element is win-itembox,
                                    // for header, they should both be the win-groupheader
                                    this._drawFocusRectangle(element);
                                }
                            }
                            if (this._tabManager.childFocus !== element && this._tabManager.childFocus !== winItem) {
                                this._selection._setFocused(entity, this._keyboardFocusInbound || this._selection._keyboardFocused());
                                this._keyboardFocusInbound = false;
                                if (entity.type === _UI.ObjectType.item) {
                                    element = items.itemAt(entity.index);
                                }
                                this._tabManager.childFocus = element;

                                if (that._updater) {
                                    var elementInfo = that._updater.elements[uniqueID(element)],
                                        focusIndex = entity.index;
                                    if (elementInfo && elementInfo.newIndex) {
                                        focusIndex = elementInfo.newIndex;
                                    }

                                    // Note to not set old and new focus to the same object
                                    that._updater.oldFocus = { type: entity.type, index: focusIndex };
                                    that._updater.newFocus = { type: entity.type, index: focusIndex };
                                }
                            }
                        }
                    }
                }

One big improvement could be to consider setting up a unit-test that could take the original "string" value of the entire function code, and comparing it to the current version of WinJS library you're using and fail if they're even one character different. This would allow you to detect if say a fix were applied, or you need to update our local hacked version with some remote changes... It's not pretty, but one way to avoid over-writing possible working WinJS code with our potentially not-so-future-proof hacked version.

Next one is the WinJS [ToggleSwitch].

This control just seemed to have all behavior wrong for me. So I hacked the keyDownHandler and simplified it's implementation which seems to have really made it more usable (for me).

var _ElementUtilities = WinJS.Utilities

WinJS.UI.ToggleSwitch.prototype._keyDownHandler =  function ToggleSwitch_keyDown(e) {
    if (this.disabled) {
        return;
    }

    // Toggle checked on spacebar
    if (e.keyCode === _ElementUtilities.Key.space ||
        e.keyCode === _ElementUtilities.Key.GamepadA ||
        e.keyCode === _ElementUtilities.Key.enter) {
        e.preventDefault();
        this.checked = !this.checked;
    }

}

The original had up/down/left/right configured to toggle the switch on/off which meant focus in/out was nearly impossible, it also only listened to space as a toggle option. So by removing the up/down/left/right we can navigate in/around the control and we wanted to listen to space, GamepadA, and enter to toggle the control on/off.

What else?

The WinJS control set is quite large, and I certainly haven't worked with each control in this manor, however, it's a step forward eh and if you managed to come across this random post on the interweb I hope it was useful?

Reusable React Component with Overridable Inline CSS Styles

(Comments)

React's Component model is great at allowing the component creator to define the interface that consumers of the component interact with. (What is exposed vs what is abstracted away).

If you're building a component and using any in-line styles and you're not careful you can lock the consumer of you're component out of potential customizations they may require for their specific use-case (that you can't think of or foresee). Trying to build components to be reusable and a little more OCP can be challenging especially with how difficult it can be to get css layouts the way you (or the consumer of you're component) may want...

As an example, let's create simple img component to illustrate the point.

Let's say we have the following image component.

import React from 'react';

export default class Image extends React.Component {

  render() {
    return (
      <div>
        <img src={this.props.src} />
      </div>
    );
  }

}

The above component is very simple and very specific.

Now let's say we allow our consumers to customize the height or width of the image. You may think, ok, simple we'll just allow the consumer to specify height and width as props to the component.

So the consumer could just go <Image height="20" width="20" src="someimage.png" />.

And you end up with something that could look like this.

import React from 'react';

export default class Image extends React.Component {

  render() {
    let imageStyle = {
      height: this.props.height,
      width: this.props.width
    };
    return (
      <div>
        <img src={this.props.src} style={imageStyle} />
      </div>
    );
  }
}

Now this works for a while, the consumers of you're component are happy they can control the height and width and everyone's humming along merrily.

Then someone comes to you and says they are having some layout issues and need to control something like float, or margin, or padding... This idea of extending the component with more props could become cumbersome if we have to do this for each and every potential layout option available.

How could we extend this generic pattern into something that allows the component to define a general set of happy defaults, while still giving the consumer complete control over layout?

One Possible Solution

We can use something like Object.assign() to easily accomplish this.

We can allow the consumers to pass in their own style={...} property and provide a set of sensible defaults for the component, but allow the consumer of our component to completely override a style if necessaryl

We can update our:

    let imageStyle = {
      height: this.props.height,
      width: this.props.width
    };

to the following pattern:

    let imageStyle = Object.assign(
      {},                               // target (starting with)
      { ...sensible defaults... },  // some pre-defined default React inline-style for the component
      this.props.style              // allow consumers to override properties
    );

Now if the consumer calls the component with <Image style={{height: "21px", width: "21px"}} src="someImage.png" /> the component's consumers' values will override any defaults provided. And they can extend the style with anything else they may need.

Happy Componentization!

Easily Convert CSS to React Inline Styles

(Comments)

TL;DR

Click the logo to jump the tool...

The More Info Stuff

So you're working on a React app. It's up and running in you're favorite browser but you notice an issue with some layout. You think, ok, this should be easy to fix. You open up the developer tools, hack on some CSS within the browser till you get it looking just the way you want it to. Maybe it's several CSS properties you added or tweaked so you copy each of them into the clipboard so you can transfer them back to your application.

Then you realize, these styles aren't coming from a CSS style sheet, they're in-line styles right in you're React component.

Now you're like, FINE, I'll manually translate this to React-style-inline-CSS. This is no biggie if you do it once in a while. Except that time when you missed removing a dash or mis-cased a letter or maybe you forgot a JSON comma, or left a CSS semicolon. Never happened to you? Oh, you are so amazing if only I was as super cool as you. For myself and probably another 1 or 2 of you out there these problems do come up, but don't have to.

I hacked together a little tool that automates this translation. Allows you to paste you're CSS into a textarea, it translates to React inline style JSON CSS and you can copy it out while avoiding translation bugs.

You can see the project here: CssToReact If you have a suggestion or want to pull-request it your self you can check it out here: Source to Project

Aside: This should really be a plugin to my text editor where we can right click and say "Paste as React Style" instead, but for now it's a single simple little web page that will automate the translation for you. (I haven't looked for the plugin - if it exists or ever is created let me know in the comments...)

Happy CSS Conversions!

Developer Friendly React Component Errors

(Comments)

One of the biggest pain points I've run into while building an application with Facebook's React is when you goof something up and you get an error in one of the React component lifecycle methods such as render, componentWillMount, componentDidUpdate, etc. The biggest problem is the lack of a feedback loop because React is swallowing exceptions, so you don't see the reported error in your developer console or any global error handlers called. There's even a chance you don't know something is going wrong (yet).

If I google for react try catch the first search result hit landed me on this GitHub issue on error boundaries (status: open as of this writing). There is a pull-request with what looks to be a potential work around, but until this lands and it provides enough of a solution I hope the below can help you.

If you read the comments of this post you'll see this helpful comment where Skiano links to a github repo with a pretty good wrapper that re-writes React components so the lifecycle methods get a useful try/catch and can properly log errors.

I liked the approach provided above but since I'm working on a project that is using BabelJS and ES6/7, I wanted to see if I could try using the new ES7 Decorators which Babel supports to allow tagging certain ES6 React classes with this try/catch wrapper.

Below is what it looks like if you end up using it.

Usage with an es7 @decorator

import React from 'react';
import wrapReactLifecycleMethodsWithTryCatch from 'react-component-errors'

@wrapReactLifecycleMethodsWithTryCatch
class MyComponent : React.Component {
  componentDidMount(){
    throw new Error("Test error");
  }
  render(){
    return <div>Hello</div>;
  }
}

But you can also use this without the decorator pattern just by passing the class through the wrapper function.

Usage without a decorator

import wrapReactComponentMethodsWithTryCatch from `react-log-errors.js`

var MyComponent = React.createClass({
  componentDidMount: function () {
      throw new Error("Test error");
  },
  render: function() {
    return <div>Hello</div>;
  }
});

wrapReactComponentMethodsWithTryCatch(MyComponent);

How to get it?

NOT tested for performance...

FYI: this is primarily built as a development tool and has not been performance tested. While I haven't noticed any performance issues - I wouldn't recommend sending to production as is without a deeper impact analysis.

Happy React Debugging!

Integrate WinJS.Navigation with the Browser's History

(Comments)

I've been playing with WinJS a bit lately, specifically the React-WinJS and wanted the native WinJS Navigation to play a little nicer with a web browser. The original/default environment for WinJS app is within a WinRT/Metro application where there is no "url/address" bar to be seen.

My uneducated guess is that the WinJS team decided not to worry about how WinJS.Navigation would integrate with a normal browser's history as there doesn't appear to be native integration or documentation about how to do it so far.

I asked the team if they had plans to work on any integration options, but only asked that last night so don't expect to hear back from over the weekend.

UPDATE: I got tired of updating this blog post with my bug fixes/iterations of the idea - so I've moved it over to GitHub: github.com/staxmanade/WinJSBrowserHistory.

So I spent a moment and prototyped one possible solution which works for this simple test using the browser's history api since I'm not looking to support browsers older than IE 10.

Ideally we could leverage WinJS controls without worrying about how to "integrate" the WinJS.Navigation with anything, but sadly some of the WinJS controls take a dependency on WinJS.Navigation (like the BackButton) so finding a way to play nice with this can be challenging.

If you want to get this prototype running yourself, you can:

  1. save both files below to a folder
  2. start up a simple web server. (I like to use nws)

This prototype is 2 files:

  • index.html <-- basic JSPM bootstrapping and configuration
  • app.jsx <-- my whole navigation app in here...

index.html

Couple mentions on this bootstrapping code:

  1. I set the background style to black (since in app.jsx I'm using the WinJS dark css) - this avoids a flash from white to black when the page loads
  2. This is using SystemJS which makes it really easy to prototype and bootstrap dependencies like WinJS and React. Please don't deploy something like this to production - follow proper JSPM production workflow procedures...
  3. The map section in the System.config defines a pointer to a fork of react-winjs I have that supports React 0.14 (so if you find this in the future and need it, try to use the native react-winjs if they've merged in my pull request instead.)
<!DOCTYPE html>
<html>
  <head>
    <style media="screen"> body{ background-color: black; } </style>
    <script src="https://jspm.io/system@0.18.js"></script>
    <script type="text/javascript">
      System.config({
        transpiler: 'babel',
        packages: {
          './': {
            defaultExtension: false
          }
        },
        map: {
          'react': 'npm:react@0.14',
          'react-winjs': 'github:staxmanade/react-winjs@2.4.0-react-0.14',
        }
      });

      System.import('./app.jsx');

    </script>
  </head>
  <body>
    <div id="main"></div>
  </body>
</html>

app.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import WinJS from 'npm:winjs';
import 'npm:winjs/css/ui-dark.css!';
import { BackButton } from 'react-winjs';


class WinJSBrowserHistory {
    isNavigationBeingHandled;
    isWinJSNavigationBackBeingHandled;
    isNavigationTriggeredByPopStateEvent;

    constructor(onApplyNavigaitonChange) {

        if(typeof onApplyNavigaitonChange !== "function") {
          throw new Error("Expecting first argumet to be a function that can take 2 parametes (location, state) => {}");
        }

        this.onApplyNavigaitonChange = onApplyNavigaitonChange;

        WinJS.Navigation.addEventListener("beforenavigate", this.handleBeforeNavigate.bind(this));
        WinJS.Navigation.addEventListener("navigating", this.handleNavigating.bind(this));
        WinJS.Navigation.addEventListener("navigated", this.handleNavigated.bind(this));

        window.addEventListener('popstate', (eventObject) => {
            console.log('popstate', this.isNavigationBeingHandled, eventObject);

            if(!this.isNavigationBeingHandled && !this.isWinJSNavigationBackBeingHandled) {
              this.handlePopState(eventObject);
            }
            this.isWinJSNavigationBackBeingHandled = false;
        })
    }

    cleanup() {
      WinJS.Navigation.removeEventListener("navigated", this.handleNavigated);
    }


    handlePopState(eventObject) {
      console.log("handlePopState", eventObject, location.hash);

      this.isNavigationTriggeredByPopStateEvent = true;

      WinJS.Navigation.navigate(location.hash, location.state);
    }

    handleBeforeNavigate(eventObject) {
        this.isNavigationBeingHandled = true;
        console.log("handleBeforeNavigate:", eventObject);
    }

    handleNavigating(eventObject) {
        console.log("handleNavigating:", eventObject);
        console.log("handleNavigating delta:", eventObject.detail.delta);

        var location = eventObject.detail.location;
        var state = eventObject.detail.state;
        var delta = eventObject.detail.delta;

        this.onApplyNavigaitonChange(location, state);

        if(delta < 0) {
            this.isWinJSNavigationBackBeingHandled = true;
            window.history.go(delta);
        } else {
            //router.setRoute(location);
            window.history.pushState(state, "", "#" + location);
        }
    }

    handleNavigated(eventObject) {
        this.isNavigationBeingHandled = false;
        this.isNavigationTriggeredByPopStateEvent = false;

        console.log("handleNavigated", eventObject);
    }

}


export default class App extends React.Component {

    constructor(props) {
        super(props);

        this.winJSBrowserHistory = new WinJSBrowserHistory(this.onApplyNavigaitonChange.bind(this));

        this.state = {
            nav: {
                state: WinJS.Navigation.state,
                location: WinJS.Navigation.location
            }
        }
    }

    componentWillMount () {
        console.log("componentWillMount");
        WinJS.Navigation.navigate(this.state.nav.location, this.state.nav.state);
    }

    componentWillUnmount () {
        console.log("componentWillUnmount");
    }

    onApplyNavigaitonChange(location, state) {
        this.setState({
            nav: {
                location: location,
                state: state
            }
        });
    }

    gotoPage1Nested() {
        WinJS.Navigation.navigate("/page1/nested");
    }

    gotoPage1() {
        WinJS.Navigation.navigate("/page1");
    }

    render() {

        console.log("render() location:", this.state.nav.location);

        var componentWithBackButton = component => {
            return (
                <div>
                    <BackButton />
                    <div>
                        {component}
                    </div>
                </div>
            );
        };

        var page;

        switch(this.state.nav.location) {
            case "/page1":
                page = componentWithBackButton(<div>page 1<br/> <button type="button" onClick={this.gotoPage1Nested.bind(this)}>Goto Page 1 Nested</button></div>);
            break;

            case "/page1/nested":
                page = componentWithBackButton(<div>page 1 nested</div>);
            break;

            case "/page2":
                page = componentWithBackButton(<div>page 1</div>);
            break;

            default:
                page = (
                    <div>
                      <button type="button" onClick={this.gotoPage1.bind(this)}>Goto Page 1</button>
                    </div>
                );
        }

        return page;
    }
}

ReactDOM.render(<App />, document.getElementById('main'));

Next I'd like to see if I could leverage something like flatiron/director for routing and get it to play nice with WinJS.Navigation and if I do, I'll post it as well...

Hope this helps.

In App Unit test example with JSPM, React, WinJS and Mocha

(Comments)

A while back I wrote about In App Unit Tests and have been having too much fun creating little starter plunks on Plunker. So...

As a simple demonstration on in-app unit tests I've thrown together a little plunk that shows in-app tests within the a WinJS Pivot. And add to the layers of abstraction I'm using react-winjs which I'm LOVING over normal WinJS development.

Link to: JSPM/React/WinJS/Mocha Plunk

If you like this starter, I have it and a few more linked here: for re-use

I'd like to highlight this particular starter at bit more in this post, not only because there are a few more concepts to this basic plunk, but also because I'm fairly happy with the MochaJS React component that I've now copied around and re-used a few times in some small projects where use In App Unit Tests.

Plunker file overview

  • index.html - The index file is a very basic JSPM bootstrap page that loads the app.jsx react component.
  • app.jsx - Defines a WinJS Pivot control where we render the in-app MochaTests.jsx React component. This also defines the test file(s) and using MochaJS's global detection we can tell the react MochaTests component what test files to load and run as well as what globals are allowed to exist.
  • config.js - This is JSPM's config that defines what version of React we're using and to use babel for transpilation.
  • tests.js - is our Mocha set of unit tests. We can have multiple test files if we want, just have to define what files to load in app.jsx.

Lastly the MochaTests.jsx which I'll include the full component below:

easier to copy-paste inherit for myself in the future

import mocha from 'mocha';
import React from 'react';

export default class MochaTests extends React.Component {

    static get propTypes() {
        return {
            testScripts: React.PropTypes.array.isRequired,
            allowedGlobals: React.PropTypes.array
        };
    }

    constructor(props) {
        super(props);
    }

    componentDidMount() {

        var testScripts = this.props.testScripts;
        var runTests = this.runTests.bind(this);

        // for some reason importing mocha with JSPM and ES6 doesn't
        // place the mocha globals on the window object. The below
        // handles that for us - as well as setting up the rest of the
        // test scripts for the first run
        mocha.suite.on('pre-require', context => {
            var exports = window;

            exports.afterEach = context.afterEach || context.teardown;
            exports.after = context.after || context.suiteTeardown;
            exports.beforeEach = context.beforeEach || context.setup;
            exports.before = context.before || context.suiteSetup;
            exports.describe = context.describe || context.suite;
            exports.it = context.it || context.test;
            exports.setup = context.setup || context.beforeEach;
            exports.suiteSetup = context.suiteSetup || context.before;
            exports.suiteTeardown = context.suiteTeardown || context.after;
            exports.suite = context.suite || context.describe;
            exports.teardown = context.teardown || context.afterEach;
            exports.test = context.test || context.it;
            exports.run = context.run;

            // now use SystemJS to load all test files
            Promise
            .all(testScripts.map(function(testScript) {
                console.log("Adding Mocha Test File: ", testScript);
                return System.import(testScript);
            })).then(runTests, function(err) {
                console.error("Error loading test modules");
                console.error(err);
            });

        });
        mocha.setup('bdd');
    }

    runTests() {
        var allowedGlobals = this.props.allowedGlobals || [];

        this.refs.mocha.getDOMNode().innerHTML = "";

        mocha.checkLeaks();
        mocha.globals(allowedGlobals);
        mocha.run();

    }

    render() {
        return (
            <div>
                <style>{"\
                  #mocha-stats em { \
                      color: inherit; \
                  } \
                  #mocha-stats { \
                    position: inherit; \
                  }\
                  #mocha .test.fail pre { \
                      color: red; \
                  } \
                "}</style>

                <button onClick={this.runTests.bind(this)}>Rerun Tests</button>

                <div id="mocha" ref="mocha"></div>
            </div>
        );
    }
}

Usage example of this React MochaTests component.

// Define what test files get loaded by the MochaTests component
var testScripts = [
  './tests.js'
];


var allowedTestGlobals = [
  // Declare what globals are allowed to be created during any test runs.
];


// Usage of MochaTests in a react render() method.
<MochaTests testScripts={testScripts} allowedGlobals={allowedTestGlobals} />

I'm not expecting to see a large up-tick in WinJS apps out there with in-app unit tests that run in the browser, however hopefully the MochaTests.jsx React Component is of value to you and can be utilized outside of WinJS within almost any React app.

Please drop a line if you end up using it or if it can be adapted. If there's value in the component, maybe

Known Issue

If the number of tests starts to go beyond the height of the pivot in this sample, it has an issue where the WinJS Pivot cuts off at the bottom not allowing you to scroll and see the rest of the test output. I haven't dug into it yet because I've been clicking the failures: X link and it filters the U.I. to just the erroring tests.

If you happen to come up with a good solution, drop me a note - I'd love it. Thanks in advance!

Happy Testing!

JSMP/SystemJS Starter Plunker

(Comments)

I'm writing this post more for myself as a quick way to get going with JSPM, but if you find it useful please feel free.

Since I've recently been playing with JSPM, I've found it useful to kick-start my prototyping with a plunk. I've created a number of starter plunks below (and continue to add to the list.

Full list of JSPM starter plunks

JSPM Starter

JSPM, React

JSPM, MochaJS

JSPM, React, MochaJS

JSPM, React, MochaJS, (Sample jsx Component test)

JSPM, React, WinJS

JSPM, React, WinJS, MochaJS


How to use:

  1. Click one of the links above
  2. Review the various files. Open and edit the app.js file
  3. Start writing code!

Be sure to open your developer tools and monitor any console output for errors...

If you want to take more of a test driven approach to your prototype take a look at MochaJS with JSPM or go directly to the JSPM/MochaJS Plunk

Happy Prototyping!