-
Notifications
You must be signed in to change notification settings - Fork 474
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented VUI for buttons and added unit tests.
- Loading branch information
Showing
3 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); |