From efc86b35264a7d8c55c68d282d75e4b181bbd17c Mon Sep 17 00:00:00 2001 From: Topher Fangio Date: Thu, 27 Aug 2015 14:25:29 -0500 Subject: [PATCH] fix(mdChips): Backspace key and custom input focus/blur. * Fix backspace key in Firefox to not change browser history/url In Firefox, the backspace key would also modify the browser's history/url. * Fix focus/blur on chips with custom input: Chips with a custom input did not properly update it's class list when the inner input was focused/blurred. Fixes #3562. References #3960. Fixes #2607. --- src/components/chips/chips.spec.js | 70 ++++++++++++++++++++++ src/components/chips/js/chipsController.js | 16 +++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/components/chips/chips.spec.js b/src/components/chips/chips.spec.js index 847dfe8ed1a..1e519f4208e 100644 --- a/src/components/chips/chips.spec.js +++ b/src/components/chips/chips.spec.js @@ -217,6 +217,58 @@ describe('', function() { expect(scope.appendChip).toHaveBeenCalled(); expect(scope.appendChip.calls.mostRecent().args[0]).toBe('Apple'); }); + + it('should prevent the default when backspace is pressed', inject(function($mdConstant) { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); + + var backspaceEvent = { + type: 'keydown', + keyCode: $mdConstant.KEY_CODE.BACKSPACE, + which: $mdConstant.KEY_CODE.BACKSPACE, + preventDefault: jasmine.createSpy('preventDefault') + }; + + element.find('input').triggerHandler(backspaceEvent); + + expect(backspaceEvent.preventDefault).toHaveBeenCalled(); + })); + + describe('with input text', function() { + + it('should prevent the default when enter is pressed', inject(function($mdConstant) { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); + + var enterEvent = { + type: 'keydown', + keyCode: $mdConstant.KEY_CODE.ENTER, + which: $mdConstant.KEY_CODE.ENTER, + preventDefault: jasmine.createSpy('preventDefault') + }; + + ctrl.chipBuffer = 'Test'; + element.find('input').triggerHandler(enterEvent); + + expect(enterEvent.preventDefault).toHaveBeenCalled(); + })); + }); + + it('focuses/blurs the component when focusing/blurring the input', inject(function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); + + // Focus the input and check + element.find('input').triggerHandler('focus'); + expect(ctrl.inputHasFocus).toBe(true); + expect(element.find('md-chips-wrap').hasClass('md-focused')).toBe(true); + + // Blur the input and check + element.find('input').triggerHandler('blur'); + expect(ctrl.inputHasFocus).toBe(false); + expect(element.find('md-chips-wrap').hasClass('md-focused')).toBe(false); + })); + }); describe('custom inputs', function() { @@ -264,6 +316,24 @@ describe('', function() { \ '; + it('focuses/blurs the component when focusing/blurring the input', inject(function($timeout) { + var element = buildChips(INPUT_TEMPLATE); + var ctrl = element.controller('mdChips'); + $timeout.flush(); + + // Focus the input and check + element.find('input').triggerHandler('focus'); + $timeout.flush(); + expect(ctrl.inputHasFocus).toBe(true); + expect(element.find('md-chips-wrap').hasClass('md-focused')).toBe(true); + + // Blur the input and check + element.find('input').triggerHandler('blur'); + $timeout.flush(); + expect(ctrl.inputHasFocus).toBe(false); + expect(element.find('md-chips-wrap').hasClass('md-focused')).toBe(false); + })); + describe('using ngModel', function() { it('should add the ngModelCtrl.$viewValue when is pressed', inject(function($timeout) { diff --git a/src/components/chips/js/chipsController.js b/src/components/chips/js/chipsController.js index 9db0aae7d15..3956d8a2067 100644 --- a/src/components/chips/js/chipsController.js +++ b/src/components/chips/js/chipsController.js @@ -92,6 +92,7 @@ function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout) { */ MdChipsCtrl.prototype.inputKeydown = function(event) { var chipBuffer = this.getChipBuffer(); + switch (event.keyCode) { case this.$mdConstant.KEY_CODE.ENTER: if ((this.hasAutocomplete && this.requireMatch) || !chipBuffer) break; @@ -101,6 +102,7 @@ MdChipsCtrl.prototype.inputKeydown = function(event) { break; case this.$mdConstant.KEY_CODE.BACKSPACE: if (chipBuffer) break; + event.preventDefault(); event.stopPropagation(); if (this.items.length) this.selectAndFocusChipSafe(this.items.length - 1); break; @@ -373,14 +375,20 @@ MdChipsCtrl.prototype.configureUserInput = function(inputElement) { this.userInputNgModelCtrl = ngModelCtrl; } - // Bind to keydown and focus events of input var scope = this.$scope; var ctrl = this; + + // Run all of the events in a digest loop + var scopeApplyFn = function(event, fn) { + scope.$apply(angular.bind(ctrl, fn, event)); + }; + + // Bind to keydown and focus events of input inputElement .attr({ tabindex: 0 }) - .on('keydown', function(event) { scope.$apply( angular.bind(ctrl, function() { ctrl.inputKeydown(event); })) }) - .on('focus', angular.bind(ctrl, ctrl.onInputFocus)) - .on('blur', angular.bind(ctrl, ctrl.onInputBlur)); + .on('keydown', function(event) { scopeApplyFn(event, ctrl.inputKeydown) }) + .on('focus', function(event) { scopeApplyFn(event, ctrl.onInputFocus) }) + .on('blur', function(event) { scopeApplyFn(event, ctrl.onInputBlur) }) }; MdChipsCtrl.prototype.configureAutocomplete = function(ctrl) {