Skip to content

Commit

Permalink
fix(chips): keypress handler for chips separator keys
Browse files Browse the repository at this point in the history
Chips separator keys can now either be numeric (and matched in the
keydown handler) or string (and matched in the keypress handler).

Closes angular#8319
  • Loading branch information
vbraun committed May 7, 2016
1 parent f5af7a1 commit 50e929c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 11 deletions.
64 changes: 59 additions & 5 deletions src/components/chips/chips.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,26 +507,80 @@ describe('<md-chips>', function() {
'<md-chips ng-model="items" md-separator-keys="keys"></md-chips>';

it('should create a new chip when a comma is entered', inject(function($mdConstant) {
scope.keys = [$mdConstant.KEY_CODE.ENTER, $mdConstant.KEY_CODE.COMMA];
scope.keys = [$mdConstant.KEY_CODE.ENTER, ','];
var element = buildChips(SEPARATOR_KEYS_CHIP_TEMPLATE);
var ctrl = element.controller('mdChips');

var commaInput = {
// Event sequence when pressing the comma key: first commaKeydown, then commaKeypress
var commaAscii = ','.charCodeAt(0);
var commaKeydown = {
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.COMMA,
charCode: 0,
which: $mdConstant.KEY_CODE.COMMA,
preventDefault: jasmine.createSpy('preventDefault')
};
var commaKeypress = {
type: 'keypress',
keyCode: commaAscii,
charCode: commaAscii,
which: commaAscii,
preventDefault: jasmine.createSpy('preventDefault')
};

ctrl.chipBuffer = 'Test';
element.find('input').triggerHandler(commaKeydown);
element.find('input').triggerHandler(commaKeypress);

expect(commaKeydown.preventDefault).not.toHaveBeenCalled();
expect(commaKeypress.preventDefault).toHaveBeenCalled();
}));

it('should not create a new chip when shift+comma is pressed', inject(function($mdConstant) {
scope.keys = [$mdConstant.KEY_CODE.ENTER, ','];
var element = buildChips(SEPARATOR_KEYS_CHIP_TEMPLATE);
var ctrl = element.controller('mdChips');

// Event sequence when pressing the comma key:
// first shiftKeydown, then commaKeydown, then shiftCommaKeypress
var shiftKeyCode = 16;
var commaAscii = ','.charCodeAt(0);
var shiftCommaAscii = '<'.charCodeAt(0); // depends on keyboard, example is dvorak
var shiftKeydown = {
type: 'keydown',
keyCode: shiftKeyCode,
charCode: 0,
which: shiftKeyCode,
preventDefault: jasmine.createSpy('preventDefault')
};
var commaKeydown = {
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.COMMA,
charCode: 0,
which: $mdConstant.KEY_CODE.COMMA,
preventDefault: jasmine.createSpy('preventDefault')
};
var shiftCommaKeypress = {
type: 'keypress',
keyCode: shiftCommaAscii,
charCode: shiftCommaAscii,
which: shiftCommaAscii,
preventDefault: jasmine.createSpy('preventDefault')
};

ctrl.chipBuffer = 'Test';
element.find('input').triggerHandler(commaInput);
element.find('input').triggerHandler(shiftKeydown);
element.find('input').triggerHandler(commaKeydown);
element.find('input').triggerHandler(shiftCommaKeypress);

expect(commaInput.preventDefault).toHaveBeenCalled();
expect(shiftKeydown.preventDefault).not.toHaveBeenCalled();
expect(commaKeydown.preventDefault).not.toHaveBeenCalled();
expect(shiftCommaKeypress.preventDefault).not.toHaveBeenCalled();
}));

it('supports custom separator key codes', inject(function($mdConstant) {
var semicolon = 186;
scope.keys = [$mdConstant.KEY_CODE.ENTER, $mdConstant.KEY_CODE.COMMA, semicolon];
scope.keys = [$mdConstant.KEY_CODE.ENTER, ',', semicolon];

var element = buildChips(SEPARATOR_KEYS_CHIP_TEMPLATE);
var ctrl = element.controller('mdChips');
Expand Down
13 changes: 9 additions & 4 deletions src/components/chips/demoCustomSeparatorKeys/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
.controller('CustomSeparatorCtrl', DemoCtrl);

function DemoCtrl ($mdConstant) {
// Use common key codes found in $mdConstant.KEY_CODE...
this.keys = [$mdConstant.KEY_CODE.ENTER, $mdConstant.KEY_CODE.COMMA];
// Use common numeric key codes found in $mdConstant.KEY_CODE or
// length-one strings for characters
this.keys = [$mdConstant.KEY_CODE.ENTER, ','];
this.tags = [];

// Any key code can be used to create a custom separator
// Any key code can be used to create a custom separator. Note
// that key codes ignore modifiers, so this makes shift+semicolon
// a separator, too
var semicolon = 186;
this.customKeys = [$mdConstant.KEY_CODE.ENTER, $mdConstant.KEY_CODE.COMMA, semicolon];
// Use this instead if you only want semicolon as separator
// var semicolon = ';';
this.customKeys = [$mdConstant.KEY_CODE.ENTER, ',', semicolon];
this.contacts = ['test@example.com'];
}
})();
25 changes: 24 additions & 1 deletion src/components/chips/js/chipsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout, $mdUtil) {
* @param event
*/
MdChipsCtrl.prototype.inputKeydown = function(event) {
console.log('keydown', event);
var chipBuffer = this.getChipBuffer();

// If we have an autocomplete, and it handled the event, we have nothing to do
Expand All @@ -131,13 +132,34 @@ MdChipsCtrl.prototype.inputKeydown = function(event) {
return;
}

this.inputKey(event.keyCode, event);
}

/**
* Handles the keypress event on the input element: Search for special
* separator keys.
* @param event
*/
MdChipsCtrl.prototype.inputKeypress = function(event) {
console.log('keypress', event);
this.inputKey(String.fromCharCode(event.charCode), event);
}

/**
* Common code for both inputKeydown and inputKeypress
* @param key Either a numeric keyCode (keydown) or a length-one string (keypress)
* @param event
*/
MdChipsCtrl.prototype.inputKey = function(key, event) {
var chipBuffer = this.getChipBuffer();

// By default <enter> appends the buffer to the chip list.
if (!this.separatorKeys || this.separatorKeys.length < 1) {
this.separatorKeys = [this.$mdConstant.KEY_CODE.ENTER];
}

// Support additional separator key codes in an array of `md-separator-keys`.
if (this.separatorKeys.indexOf(event.keyCode) !== -1) {
if (this.separatorKeys.indexOf(key) !== -1) {
if ((this.hasAutocomplete && this.requireMatch) || !chipBuffer) return;
event.preventDefault();

Expand Down Expand Up @@ -527,6 +549,7 @@ MdChipsCtrl.prototype.configureUserInput = function(inputElement) {
inputElement
.attr({ tabindex: 0 })
.on('keydown', function(event) { scopeApplyFn(event, ctrl.inputKeydown) })
.on('keypress', function(event) { scopeApplyFn(event, ctrl.inputKeypress) })
.on('focus', function(event) { scopeApplyFn(event, ctrl.onInputFocus) })
.on('blur', function(event) { scopeApplyFn(event, ctrl.onInputBlur) })
};
Expand Down
5 changes: 4 additions & 1 deletion src/components/chips/js/chipsDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@
* the delete key will remove the chip.
* @param {string=} delete-button-label A label for the delete button. Also hidden and read by
* screen readers.
* @param {expression=} md-separator-keys An array of key codes used to separate chips.
* @param {expression=} md-separator-keys An array of keys used to separate chips. Each entry is
* either a numeric key code (triggering on keydown, ignoring modifiers) or a length-one
* string (matching that keypress).
*
* @usage
* <hljs lang="html">
Expand Down Expand Up @@ -155,6 +157,7 @@
ng-focus="$mdChipsCtrl.onInputFocus()"\
ng-blur="$mdChipsCtrl.onInputBlur()"\
ng-trim="false"\
ng-keypress="$mdChipsCtrl.inputKeypress($event)"\
ng-keydown="$mdChipsCtrl.inputKeydown($event)">';

var CHIP_DEFAULT_TEMPLATE = '\
Expand Down

0 comments on commit 50e929c

Please sign in to comment.