Skip to content

Commit

Permalink
Implemented VUI for buttons and added unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
jseanxu committed Jul 30, 2015
1 parent 32fc70e commit adae969
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 0 deletions.
127 changes: 127 additions & 0 deletions src/js/WinJS/Vui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information.
import _Global = require("./Core/_Global");

import _BaseUtils = require("./Core/_BaseUtils");
import _ElementUtilities = require("./Utilities/_ElementUtilities");

interface ListeningModeEvent extends Event {
label: string;
state: string;
}

interface ListeningModeTransitionHandler {
/**
* Transitions the element from 'inactive' to 'active' state
**/
activate(element: HTMLElement, label: string): void;

/**
* Transitions the element from 'active' to 'disambiguation' state
**/
disambiguate(element: HTMLElement, label: string): void;

/**
* Transitions the element from 'disambiguation' to 'active' state
**/
reactivate(element: HTMLElement, label: string): void;

/**
* Transitions the element to the 'inactive' state
**/
deactivate(element: HTMLElement): void;
}

var Properties = {
origContent: "_vuiOrigContent"
};

var ClassNames = {
active: "win-vui-active",
disambiguation: "win-vui-disambiguation",
};

var EventNames = {
ListeningModeStateChanged: "listeningmodestatechanged"
};

var ListeningModeStates = {
active: "active",
disambiguation: "disambiguation",
inactive: "inactive",
};

function _handleListeningModeStateChanged(e: ListeningModeEvent) {
var target = <HTMLElement>e.target;
var transitionHandler: ListeningModeTransitionHandler = Handlers[target.tagName];
if (!transitionHandler) {
return;
}

switch (e.state) {
case ListeningModeStates.active:
if (target[Properties.origContent] || _ElementUtilities.hasClass(target, ClassNames.active)) {
_ElementUtilities.removeClass(target, ClassNames.disambiguation);
transitionHandler.reactivate(target, e.label);
} else {
_ElementUtilities.addClass(target, ClassNames.active);
transitionHandler.activate(target, e.label);
}
break;

case ListeningModeStates.disambiguation:
_ElementUtilities.addClass(target, ClassNames.active);
_ElementUtilities.addClass(target, ClassNames.disambiguation);
transitionHandler.disambiguate(target, e.label);
break;

case ListeningModeStates.inactive:
_ElementUtilities.removeClass(target, ClassNames.active);
_ElementUtilities.removeClass(target, ClassNames.disambiguation);
transitionHandler.deactivate(target);
break;
}
}

module Handlers {
// The static classes in this module correspond to the element tag name they are handling
export class BUTTON {
static activate(element: HTMLElement, label: string) {
var origContent: any;
if (element.childElementCount) {
origContent = [];
while (element.childElementCount) {
origContent.push(element.removeChild(element.children[0]));
}
} else {
origContent = element.innerHTML;
}
element[Properties.origContent] = origContent;
element.textContent = label;
}

static disambiguate(element: HTMLElement, label: string) {
element.textContent = label;
}

static reactivate(element: HTMLElement, label: string) {
element.textContent = label;
}

static deactivate(element: HTMLElement) {
var origContent = element[Properties.origContent];
if (typeof origContent === "string") {
element.innerHTML = origContent;
} else {
element.innerHTML = "";
origContent.forEach((node: Node) => element.appendChild(node));
}
delete element[Properties.origContent];
}
}
}


if (_Global.document) {
// We are subscribing for the capture phase to allow subsequent handlers to overwrite the default behavior.
_Global.document.addEventListener(EventNames.ListeningModeStateChanged, _handleListeningModeStateChanged, true);
}
1 change: 1 addition & 0 deletions src/js/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
define([
'WinJS/Core/_WinJS',
'WinJS/VirtualizedDataSource',
'WinJS/Vui',
'WinJS/Controls/IntrinsicControls',
'WinJS/Controls/ListView',
'WinJS/Controls/FlipView',
Expand Down
174 changes: 174 additions & 0 deletions tests/UI/vui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information.
// <reference path="ms-appx://$(TargetFramework)/js/WinJS.js" />
// <reference path="ms-appx://$(TargetFramework)/css/ui-dark.css" />

/// <deploy src="../TestData/" />

module WinJSTests {
interface ListeningModeStateChangedEvent extends CustomEvent {
label: string;
state: string;
}

var ListeningModeStateChangedEventName = "listeningmodestatechanged";
var DisambiguationText = "Item One";

function fireActiveMode(element: HTMLElement, label: string) {
var e = <ListeningModeStateChangedEvent>document.createEvent("CustomEvent");
e.initCustomEvent(ListeningModeStateChangedEventName, true, false, null);
e.state = "active";
e.label = label;
element.dispatchEvent(e);
}

function fireDisambigMode(element: HTMLElement, label: string) {
var e = <ListeningModeStateChangedEvent>document.createEvent("CustomEvent");
e.initCustomEvent(ListeningModeStateChangedEventName, true, false, null);
e.state = "disambiguation";
e.label = label;
element.dispatchEvent(e);
}

function fireInactiveMode(element: HTMLElement) {
var e = <ListeningModeStateChangedEvent>document.createEvent("CustomEvent");
e.initCustomEvent(ListeningModeStateChangedEventName, true, false, null);
e.state = "inactive";
e.label = null;
element.dispatchEvent(e);
}

function simulateVuiStateChanges(
element: HTMLElement,
activeHandler: (e: ListeningModeStateChangedEvent) => any,
disambigHandler: (e: ListeningModeStateChangedEvent) => any,
inactiveHandler: (e: ListeningModeStateChangedEvent) => any,
completeHandler?: Function) {

function handler(e: ListeningModeStateChangedEvent) {
switch (e.state) {
case "active":
activeHandler(e);
break;

case "disambiguation":
disambigHandler(e);
break;

case "inactive":
inactiveHandler(e);

break;
}
}

element.addEventListener(ListeningModeStateChangedEventName, handler);
var origText = element.textContent;
var activeText = element.getAttribute("aria-label")

// Inactive > Active > Inactive
fireActiveMode(element, activeText);
fireInactiveMode(element);

// Inactive > Active > Disambiguation > Inactive
fireActiveMode(element, activeText);
fireDisambigMode(element, DisambiguationText);
fireInactiveMode(element);

// Inactive > Active > Disambiguation > Active > Inactive
fireActiveMode(element, activeText);
fireDisambigMode(element, DisambiguationText);
fireActiveMode(element, activeText);
fireInactiveMode(element);

// Inactive > Active > Disambiguation > Active > Disambiguation > Inactive
fireActiveMode(element, activeText);
fireDisambigMode(element, DisambiguationText);
fireActiveMode(element, activeText);
fireDisambigMode(element, DisambiguationText);
fireInactiveMode(element);
completeHandler && completeHandler();
}

export class VUITests {
testContainer: HTMLElement;

setUp() {
this.testContainer = document.createElement("div");
document.body.appendChild(this.testContainer);
}

tearDown() {
this.testContainer.parentElement.removeChild(this.testContainer);
this.testContainer = null;
}

testButton() {
var inactiveText = "Inactive Text";
var activeText = "Active Text";

var button = document.createElement("button");
button.textContent = inactiveText;
button.setAttribute("aria-label", activeText);
this.testContainer.appendChild(button);

simulateVuiStateChanges(button,
e => {
// Active
LiveUnit.Assert.isTrue(button.classList.contains("win-vui-active"));
LiveUnit.Assert.isFalse(button.classList.contains("win-vui-disambiguation"));
LiveUnit.Assert.areEqual(activeText, button.textContent);
},
e => {
// Disambig
LiveUnit.Assert.isTrue(button.classList.contains("win-vui-active"));
LiveUnit.Assert.isTrue(button.classList.contains("win-vui-disambiguation"));
LiveUnit.Assert.areNotEqual(inactiveText, button.textContent);
LiveUnit.Assert.areNotEqual(activeText, button.textContent);
},
e => {
// Inactive
LiveUnit.Assert.isFalse(button.classList.contains("win-vui-active"));
LiveUnit.Assert.isFalse(button.classList.contains("win-vui-disambiguation"));
LiveUnit.Assert.areEqual(inactiveText, button.textContent);
});
}

testButtonWithChildren() {
var activeText = "Active Text";

var span1 = document.createElement("span");
span1.textContent = "Hello";

var span2 = document.createElement("span");
span2.textContent = "World";

var button = document.createElement("button");
button.setAttribute("aria-label", activeText);
button.appendChild(span1);
button.appendChild(span2);
this.testContainer.appendChild(button);

var inactiveText = button.textContent;

simulateVuiStateChanges(button,
e => {
// Active
LiveUnit.Assert.areEqual(activeText, button.textContent);
LiveUnit.Assert.areNotEqual(inactiveText, button.textContent);
},
e => {
// Disambig
LiveUnit.Assert.areNotEqual(inactiveText, button.textContent);
LiveUnit.Assert.areNotEqual(activeText, button.textContent);
},
e => {
// Inactive
LiveUnit.Assert.areEqual(inactiveText, button.textContent);
LiveUnit.Assert.areEqual(2, button.childElementCount);
LiveUnit.Assert.isTrue(button.children[0] === span1);
LiveUnit.Assert.isTrue(button.children[1] === span2);
});
}
}
}
LiveUnit.registerTestClass("WinJSTests.VUITests");

0 comments on commit adae969

Please sign in to comment.