forked from angular/angular
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 angular#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 angular#523 close angular#1136
- Loading branch information
Showing
8 changed files
with
395 additions
and
2 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
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
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,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) { | ||
eventName = eventName.toLowerCase(); | ||
var parts = eventName.split('.'); | ||
var domEventName = ListWrapper.removeAt(parts, 0); | ||
if ((parts.length === 0) || (domEventName !== 'keydown' && 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 (key == ' ') { | ||
key = 'space'; // for readability | ||
} else if (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)); | ||
} | ||
}; | ||
} | ||
} |
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,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(''); | ||
}); | ||
|
||
}); |
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,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> |
Oops, something went wrong.