diff --git a/src/components/chips/chips.spec.js b/src/components/chips/chips.spec.js index c85e24485e2..8047a1f7292 100755 --- a/src/components/chips/chips.spec.js +++ b/src/components/chips/chips.spec.js @@ -507,26 +507,80 @@ describe('', function() { ''; 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'); diff --git a/src/components/chips/demoCustomSeparatorKeys/script.js b/src/components/chips/demoCustomSeparatorKeys/script.js index a389b223cbe..38fb923b685 100644 --- a/src/components/chips/demoCustomSeparatorKeys/script.js +++ b/src/components/chips/demoCustomSeparatorKeys/script.js @@ -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']; } })(); diff --git a/src/components/chips/js/chipsController.js b/src/components/chips/js/chipsController.js index 01c6e112eb4..e911d748345 100644 --- a/src/components/chips/js/chipsController.js +++ b/src/components/chips/js/chipsController.js @@ -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 @@ -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 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(); @@ -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) }) }; diff --git a/src/components/chips/js/chipsDirective.js b/src/components/chips/js/chipsDirective.js index b4c86cd81a1..0941105fc86 100644 --- a/src/components/chips/js/chipsDirective.js +++ b/src/components/chips/js/chipsDirective.js @@ -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 * @@ -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 = '\