feat(keyEvents): support for <div (keyup.enter)="callback()">
This commit adds a plugin for the event manager, to allow a key name to be appended to the event name (for keyup and keydown events), so that the callback is only called for that key. Here are some examples: (keydown.shift.enter) (keyup.space) (keydown.control.shift.a) (keyup.f1) Key names mostly follow the DOM Level 3 event key values: http://www.w3.org/TR/DOM-Level-3-Events-key/#key-value-tables There are some limitations to be worked on (cf details in https://github.com/angular/angular/pull/1136) but for now, this implementation is reliable for the following keys (by "reliable" I mean compatible with Chrome and Firefox and not depending on the keyboard layout): - alt, control, shift, meta (those keys can be combined with other keys) - tab, enter, backspace, pause, scrolllock, capslock, numlock - insert, delete, home, end, pageup, pagedown - arrowup, arrowdown, arrowleft, arrowright - latin letters (a-z), function keys (f1-f12) - numbers on the numeric keypad (but those keys are not correctly simulated by Chromedriver) There is a sample to play with in examples/src/key_events/. close #523 close #1136
This commit is contained in:
parent
f45281a10a
commit
8fa1539bac
|
@ -18,6 +18,7 @@ import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_
|
||||||
import {XHR} from 'angular2/src/services/xhr';
|
import {XHR} from 'angular2/src/services/xhr';
|
||||||
import {XHRImpl} from 'angular2/src/services/xhr_impl';
|
import {XHRImpl} from 'angular2/src/services/xhr_impl';
|
||||||
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||||
|
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||||
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
||||||
import {Binding} from 'angular2/src/di/binding';
|
import {Binding} from 'angular2/src/di/binding';
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
@ -81,7 +82,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
[appComponentRefToken]),
|
[appComponentRefToken]),
|
||||||
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),[ExceptionHandler]),
|
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),[ExceptionHandler]),
|
||||||
bind(EventManager).toFactory((zone) => {
|
bind(EventManager).toFactory((zone) => {
|
||||||
var plugins = [new HammerGesturesPlugin(), new DomEventsPlugin()];
|
var plugins = [new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
||||||
return new EventManager(plugins, zone);
|
return new EventManager(plugins, zone);
|
||||||
}, [VmTurnZone]),
|
}, [VmTurnZone]),
|
||||||
bind(ShadowDomStrategy).toFactory(
|
bind(ShadowDomStrategy).toFactory(
|
||||||
|
|
|
@ -14,6 +14,87 @@ class _IdentitySanitizer implements NodeTreeSanitizer {
|
||||||
|
|
||||||
final _identitySanitizer = new _IdentitySanitizer();
|
final _identitySanitizer = new _IdentitySanitizer();
|
||||||
|
|
||||||
|
final _keyCodeToKeyMap = const {
|
||||||
|
8: 'Backspace',
|
||||||
|
9: 'Tab',
|
||||||
|
12: 'Clear',
|
||||||
|
13: 'Enter',
|
||||||
|
16: 'Shift',
|
||||||
|
17: 'Control',
|
||||||
|
18: 'Alt',
|
||||||
|
19: 'Pause',
|
||||||
|
20: 'CapsLock',
|
||||||
|
27: 'Escape',
|
||||||
|
32: ' ',
|
||||||
|
33: 'PageUp',
|
||||||
|
34: 'PageDown',
|
||||||
|
35: 'End',
|
||||||
|
36: 'Home',
|
||||||
|
37: 'ArrowLeft',
|
||||||
|
38: 'ArrowUp',
|
||||||
|
39: 'ArrowRight',
|
||||||
|
40: 'ArrowDown',
|
||||||
|
45: 'Insert',
|
||||||
|
46: 'Delete',
|
||||||
|
65: 'a',
|
||||||
|
66: 'b',
|
||||||
|
67: 'c',
|
||||||
|
68: 'd',
|
||||||
|
69: 'e',
|
||||||
|
70: 'f',
|
||||||
|
71: 'g',
|
||||||
|
72: 'h',
|
||||||
|
73: 'i',
|
||||||
|
74: 'j',
|
||||||
|
75: 'k',
|
||||||
|
76: 'l',
|
||||||
|
77: 'm',
|
||||||
|
78: 'n',
|
||||||
|
79: 'o',
|
||||||
|
80: 'p',
|
||||||
|
81: 'q',
|
||||||
|
82: 'r',
|
||||||
|
83: 's',
|
||||||
|
84: 't',
|
||||||
|
85: 'u',
|
||||||
|
86: 'v',
|
||||||
|
87: 'w',
|
||||||
|
88: 'x',
|
||||||
|
89: 'y',
|
||||||
|
90: 'z',
|
||||||
|
91: 'OS',
|
||||||
|
93: 'ContextMenu',
|
||||||
|
96: '0',
|
||||||
|
97: '1',
|
||||||
|
98: '2',
|
||||||
|
99: '3',
|
||||||
|
100: '4',
|
||||||
|
101: '5',
|
||||||
|
102: '6',
|
||||||
|
103: '7',
|
||||||
|
104: '8',
|
||||||
|
105: '9',
|
||||||
|
106: '*',
|
||||||
|
107: '+',
|
||||||
|
109: '-',
|
||||||
|
110: '.',
|
||||||
|
111: '/',
|
||||||
|
112: 'F1',
|
||||||
|
113: 'F2',
|
||||||
|
114: 'F3',
|
||||||
|
115: 'F4',
|
||||||
|
116: 'F5',
|
||||||
|
117: 'F6',
|
||||||
|
118: 'F7',
|
||||||
|
119: 'F8',
|
||||||
|
120: 'F9',
|
||||||
|
121: 'F10',
|
||||||
|
122: 'F11',
|
||||||
|
123: 'F12',
|
||||||
|
144: 'NumLock',
|
||||||
|
145: 'ScrollLock'
|
||||||
|
};
|
||||||
|
|
||||||
class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
static void makeCurrent() {
|
static void makeCurrent() {
|
||||||
setRootDomAdapter(new BrowserDomAdapter());
|
setRootDomAdapter(new BrowserDomAdapter());
|
||||||
|
@ -203,4 +284,8 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
String getHref(AnchorElement element) {
|
String getHref(AnchorElement element) {
|
||||||
return element.href;
|
return element.href;
|
||||||
}
|
}
|
||||||
|
String getEventKey(KeyboardEvent event) {
|
||||||
|
int keyCode = event.keyCode;
|
||||||
|
return _keyCodeToKeyMap.containsKey(keyCode) ? _keyCodeToKeyMap[keyCode] : 'Unidentified';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {setRootDomAdapter} from './dom_adapter';
|
import {setRootDomAdapter} from './dom_adapter';
|
||||||
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
||||||
|
|
||||||
|
@ -9,6 +9,49 @@ var _attrToPropMap = {
|
||||||
'tabindex': 'tabIndex'
|
'tabindex': 'tabIndex'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DOM_KEY_LOCATION_NUMPAD = 3;
|
||||||
|
|
||||||
|
// Map to convert some key or keyIdentifier values to what will be returned by getEventKey
|
||||||
|
var _keyMap = {
|
||||||
|
// The following values are here for cross-browser compatibility and to match the W3C standard
|
||||||
|
// cf http://www.w3.org/TR/DOM-Level-3-Events-key/
|
||||||
|
'\b': 'Backspace',
|
||||||
|
'\t': 'Tab',
|
||||||
|
'\x7F': 'Delete',
|
||||||
|
'\x1B': 'Escape',
|
||||||
|
'Del': 'Delete',
|
||||||
|
'Esc': 'Escape',
|
||||||
|
'Left': 'ArrowLeft',
|
||||||
|
'Right': 'ArrowRight',
|
||||||
|
'Up': 'ArrowUp',
|
||||||
|
'Down':'ArrowDown',
|
||||||
|
'Menu': 'ContextMenu',
|
||||||
|
'Scroll' : 'ScrollLock',
|
||||||
|
'Win': 'OS'
|
||||||
|
};
|
||||||
|
|
||||||
|
// There is a bug in Chrome for numeric keypad keys:
|
||||||
|
// https://code.google.com/p/chromium/issues/detail?id=155654
|
||||||
|
// 1, 2, 3 ... are reported as A, B, C ...
|
||||||
|
var _chromeNumKeyPadMap = {
|
||||||
|
'A': '1',
|
||||||
|
'B': '2',
|
||||||
|
'C': '3',
|
||||||
|
'D': '4',
|
||||||
|
'E': '5',
|
||||||
|
'F': '6',
|
||||||
|
'G': '7',
|
||||||
|
'H': '8',
|
||||||
|
'I': '9',
|
||||||
|
'J': '*',
|
||||||
|
'K': '+',
|
||||||
|
'M': '-',
|
||||||
|
'N': '.',
|
||||||
|
'O': '/',
|
||||||
|
'\x60': '0',
|
||||||
|
'\x90': 'NumLock'
|
||||||
|
};
|
||||||
|
|
||||||
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
static makeCurrent() {
|
static makeCurrent() {
|
||||||
setRootDomAdapter(new BrowserDomAdapter());
|
setRootDomAdapter(new BrowserDomAdapter());
|
||||||
|
@ -286,4 +329,28 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
getHref(el:Element): string {
|
getHref(el:Element): string {
|
||||||
return el.href;
|
return el.href;
|
||||||
}
|
}
|
||||||
|
getEventKey(event): string {
|
||||||
|
var key = event.key;
|
||||||
|
if (isBlank(key)) {
|
||||||
|
key = event.keyIdentifier;
|
||||||
|
// keyIdentifier is defined in the old draft of DOM Level 3 Events implemented by Chrome and Safari
|
||||||
|
// cf http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/events.html#Events-KeyboardEvents-Interfaces
|
||||||
|
if (isBlank(key)) {
|
||||||
|
return 'Unidentified';
|
||||||
|
}
|
||||||
|
if (key.startsWith('U+')) {
|
||||||
|
key = String.fromCharCode(parseInt(key.substring(2), 16));
|
||||||
|
if (event.location === DOM_KEY_LOCATION_NUMPAD && _chromeNumKeyPadMap.hasOwnProperty(key)) {
|
||||||
|
// There is a bug in Chrome for numeric keypad keys:
|
||||||
|
// https://code.google.com/p/chromium/issues/detail?id=155654
|
||||||
|
// 1, 2, 3 ... are reported as A, B, C ...
|
||||||
|
key = _chromeNumKeyPadMap[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_keyMap.hasOwnProperty(key)) {
|
||||||
|
key = _keyMap[key];
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,9 @@ export class DomAdapter {
|
||||||
getHref(element): string {
|
getHref(element): string {
|
||||||
throw _abstract();
|
throw _abstract();
|
||||||
}
|
}
|
||||||
|
getEventKey(event): string {
|
||||||
|
throw _abstract();
|
||||||
|
}
|
||||||
resolveAndSetHref(element, baseUrl:string, href:string) {
|
resolveAndSetHref(element, baseUrl:string, href:string) {
|
||||||
throw _abstract();
|
throw _abstract();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {isPresent, isBlank, StringWrapper, RegExpWrapper, BaseException, NumberWrapper} from 'angular2/src/facade/lang';
|
||||||
|
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {EventManagerPlugin} from './event_manager';
|
||||||
|
|
||||||
|
var modifierKeys = ['alt', 'control', 'meta', 'shift'];
|
||||||
|
var modifierKeyGetters = {
|
||||||
|
'alt': (event) => event.altKey,
|
||||||
|
'control': (event) => event.ctrlKey,
|
||||||
|
'meta': (event) => event.metaKey,
|
||||||
|
'shift': (event) => event.shiftKey
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KeyEventsPlugin extends EventManagerPlugin {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(eventName: string): boolean {
|
||||||
|
return isPresent(KeyEventsPlugin.parseEventName(eventName));
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener(element, eventName: string, handler: Function,
|
||||||
|
shouldSupportBubble: boolean) {
|
||||||
|
var parsedEvent = KeyEventsPlugin.parseEventName(eventName);
|
||||||
|
|
||||||
|
var outsideHandler = KeyEventsPlugin.eventCallback(element, shouldSupportBubble,
|
||||||
|
StringMapWrapper.get(parsedEvent, 'fullKey'), handler, this.manager.getZone());
|
||||||
|
|
||||||
|
this.manager.getZone().runOutsideAngular(() => {
|
||||||
|
DOM.on(element, StringMapWrapper.get(parsedEvent, 'domEventName'), outsideHandler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseEventName(eventName: string) /* {'domEventName': string, 'fullKey': string} */ {
|
||||||
|
eventName = eventName.toLowerCase();
|
||||||
|
var parts = eventName.split('.');
|
||||||
|
var domEventName = ListWrapper.removeAt(parts, 0);
|
||||||
|
if ((parts.length === 0) || !(StringWrapper.equals(domEventName, 'keydown') || StringWrapper.equals(domEventName, 'keyup'))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var key = ListWrapper.removeLast(parts);
|
||||||
|
|
||||||
|
var fullKey = '';
|
||||||
|
ListWrapper.forEach(modifierKeys, (modifierName) => {
|
||||||
|
if (ListWrapper.contains(parts, modifierName)) {
|
||||||
|
ListWrapper.remove(parts, modifierName);
|
||||||
|
fullKey += modifierName + '.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fullKey += key;
|
||||||
|
|
||||||
|
if (parts.length != 0 || key.length === 0) {
|
||||||
|
// returning null instead of throwing to let another plugin process the event
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'domEventName': domEventName,
|
||||||
|
'fullKey': fullKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEventFullKey(event): string {
|
||||||
|
var fullKey = '';
|
||||||
|
var key = DOM.getEventKey(event);
|
||||||
|
key = key.toLowerCase();
|
||||||
|
if (StringWrapper.equals(key, ' ')) {
|
||||||
|
key = 'space'; // for readability
|
||||||
|
} else if (StringWrapper.equals(key, '.')) {
|
||||||
|
key = 'dot'; // because '.' is used as a separator in event names
|
||||||
|
}
|
||||||
|
ListWrapper.forEach(modifierKeys, (modifierName) => {
|
||||||
|
if (modifierName != key) {
|
||||||
|
var modifierGetter = StringMapWrapper.get(modifierKeyGetters, modifierName);
|
||||||
|
if (modifierGetter(event)) {
|
||||||
|
fullKey += modifierName + '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fullKey += key;
|
||||||
|
return fullKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static eventCallback(element, shouldSupportBubble, fullKey, handler, zone) {
|
||||||
|
return (event) => {
|
||||||
|
var correctElement = shouldSupportBubble || event.target === element;
|
||||||
|
if (correctElement && KeyEventsPlugin.getEventFullKey(event) === fullKey) {
|
||||||
|
zone.run(() => handler(event));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, el} from 'angular2/test_lib';
|
||||||
|
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('KeyEvents', () => {
|
||||||
|
|
||||||
|
it('should ignore unrecognized events', () => {
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown')).toEqual(null);
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup')).toEqual(null);
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.unknownmodifier.enter')).toEqual(null);
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.unknownmodifier.enter')).toEqual(null);
|
||||||
|
expect(KeyEventsPlugin.parseEventName('unknownevent.control.shift.enter')).toEqual(null);
|
||||||
|
expect(KeyEventsPlugin.parseEventName('unknownevent.enter')).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly parse event names', () => {
|
||||||
|
// key with no modifier
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.enter')).toEqual({
|
||||||
|
'domEventName': 'keydown',
|
||||||
|
'fullKey': 'enter'
|
||||||
|
});
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.enter')).toEqual({
|
||||||
|
'domEventName': 'keyup',
|
||||||
|
'fullKey': 'enter'
|
||||||
|
});
|
||||||
|
|
||||||
|
// key with modifiers:
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.control.shift.enter')).toEqual({
|
||||||
|
'domEventName': 'keydown',
|
||||||
|
'fullKey': 'control.shift.enter'
|
||||||
|
});
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.control.shift.enter')).toEqual({
|
||||||
|
'domEventName': 'keyup',
|
||||||
|
'fullKey': 'control.shift.enter'
|
||||||
|
});
|
||||||
|
|
||||||
|
// key with modifiers in a different order:
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.shift.control.enter')).toEqual({
|
||||||
|
'domEventName': 'keydown',
|
||||||
|
'fullKey': 'control.shift.enter'
|
||||||
|
});
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.shift.control.enter')).toEqual({
|
||||||
|
'domEventName': 'keyup',
|
||||||
|
'fullKey': 'control.shift.enter'
|
||||||
|
});
|
||||||
|
|
||||||
|
// key that is also a modifier:
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.shift.control')).toEqual({
|
||||||
|
'domEventName': 'keydown',
|
||||||
|
'fullKey': 'shift.control'
|
||||||
|
});
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.shift.control')).toEqual({
|
||||||
|
'domEventName': 'keyup',
|
||||||
|
'fullKey': 'shift.control'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keydown.control.shift')).toEqual({
|
||||||
|
'domEventName': 'keydown',
|
||||||
|
'fullKey': 'control.shift'
|
||||||
|
});
|
||||||
|
expect(KeyEventsPlugin.parseEventName('keyup.control.shift')).toEqual({
|
||||||
|
'domEventName': 'keyup',
|
||||||
|
'fullKey': 'control.shift'
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
var testUtil = require('angular2/src/test_lib/e2e_util');
|
||||||
|
describe('key_events', function () {
|
||||||
|
|
||||||
|
var URL = 'examples/src/key_events/index.html';
|
||||||
|
|
||||||
|
afterEach(testUtil.verifyNoBrowserErrors);
|
||||||
|
beforeEach(() => {
|
||||||
|
browser.get(URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display correct key names', function() {
|
||||||
|
var firstArea = element.all(by.css('.sample-area')).get(0);
|
||||||
|
expect(firstArea.getText()).toBe('(none)');
|
||||||
|
|
||||||
|
// testing different key categories:
|
||||||
|
firstArea.sendKeys(protractor.Key.ENTER);
|
||||||
|
expect(firstArea.getText()).toBe('enter');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.SHIFT, protractor.Key.ENTER);
|
||||||
|
expect(firstArea.getText()).toBe('shift.enter');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.CONTROL, protractor.Key.SHIFT, protractor.Key.ENTER);
|
||||||
|
expect(firstArea.getText()).toBe('control.shift.enter');
|
||||||
|
|
||||||
|
firstArea.sendKeys(' ');
|
||||||
|
expect(firstArea.getText()).toBe('space');
|
||||||
|
|
||||||
|
firstArea.sendKeys('a');
|
||||||
|
expect(firstArea.getText()).toBe('a');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.CONTROL, 'b');
|
||||||
|
expect(firstArea.getText()).toBe('control.b');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.F1);
|
||||||
|
expect(firstArea.getText()).toBe('f1');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.ALT, protractor.Key.F1);
|
||||||
|
expect(firstArea.getText()).toBe('alt.f1');
|
||||||
|
|
||||||
|
firstArea.sendKeys(protractor.Key.CONTROL, protractor.Key.F1);
|
||||||
|
expect(firstArea.getText()).toBe('control.f1');
|
||||||
|
|
||||||
|
// There is an issue with protractor.Key.NUMPAD0 (and other NUMPADx):
|
||||||
|
// chromedriver does not correctly set the location property on the event to
|
||||||
|
// specify that the key is on the numeric keypad (event.location = 3)
|
||||||
|
// so the following test fails:
|
||||||
|
// firstArea.sendKeys(protractor.Key.NUMPAD0);
|
||||||
|
// expect(firstArea.getText()).toBe('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly react to the specified key', function() {
|
||||||
|
var secondArea = element.all(by.css('.sample-area')).get(1);
|
||||||
|
secondArea.sendKeys(protractor.Key.SHIFT, protractor.Key.ENTER);
|
||||||
|
expect(secondArea.getText()).toEqual('You pressed shift.enter!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not react to incomplete keys', function() {
|
||||||
|
var secondArea = element.all(by.css('.sample-area')).get(1);
|
||||||
|
secondArea.sendKeys(protractor.Key.ENTER);
|
||||||
|
expect(secondArea.getText()).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not react to keys with more modifiers', function() {
|
||||||
|
var secondArea = element.all(by.css('.sample-area')).get(1);
|
||||||
|
secondArea.sendKeys(protractor.Key.CONTROL, protractor.Key.SHIFT, protractor.Key.ENTER);
|
||||||
|
expect(secondArea.getText()).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<title>Key events</title>
|
||||||
|
<style>
|
||||||
|
.sample-area {
|
||||||
|
text-align: center;
|
||||||
|
margin: 5px;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
}
|
||||||
|
.sample-area:focus {
|
||||||
|
border: 1px solid blue;
|
||||||
|
color: blue;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<key-events-app>
|
||||||
|
Loading...
|
||||||
|
</key-events-app>
|
||||||
|
|
||||||
|
$SCRIPTS$
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,50 @@
|
||||||
|
import {bootstrap, Component, Template} from 'angular2/angular2';
|
||||||
|
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||||
|
|
||||||
|
// 2 imports for the Dart version:
|
||||||
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'key-events-app',
|
||||||
|
})
|
||||||
|
@Template({
|
||||||
|
inline: `Click in the following area and press a key to display its name:<br>
|
||||||
|
<div (keydown)="onKeyDown($event)" class="sample-area" tabindex="0">{{lastKey}}</div><br>
|
||||||
|
Click in the following area and press shift.enter:<br>
|
||||||
|
<div
|
||||||
|
(keydown.shift.enter)="onShiftEnter($event)"
|
||||||
|
(click)="resetShiftEnter()"
|
||||||
|
class="sample-area"
|
||||||
|
tabindex="0"
|
||||||
|
>{{shiftEnter ? 'You pressed shift.enter!' : ''}}</div>`
|
||||||
|
})
|
||||||
|
class KeyEventsApp {
|
||||||
|
lastKey: string;
|
||||||
|
shiftEnter: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.lastKey = '(none)';
|
||||||
|
this.shiftEnter = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event) {
|
||||||
|
this.lastKey = KeyEventsPlugin.getEventFullKey(event);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
onShiftEnter(event) {
|
||||||
|
this.shiftEnter = true;
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetShiftEnter() {
|
||||||
|
this.shiftEnter = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities(); // for the Dart version
|
||||||
|
bootstrap(KeyEventsApp);
|
||||||
|
}
|
Loading…
Reference in New Issue