Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit cfb285c

Browse files
devversionThomasBurleson
authored andcommitted
fix(chips): fix chips focus functionality
- The normal mobile devices won't trigger the focus on the element. Focusing the element on `ng-click` fixes that issue. Through keeping and redirecting to `ng-focus` it's still possible to select chips through keyboard - At the moment, we only reset the selectedChip variable but we don't apply that to the view, so we need to run an async evaluation. Fixes #5897. Fixes #5662. Closes #5941 # Conflicts: # src/components/chips/chips.spec.js
1 parent a1ff0d5 commit cfb285c

File tree

3 files changed

+167
-5
lines changed

3 files changed

+167
-5
lines changed

src/components/chips/chips.spec.js

Lines changed: 164 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ describe('<md-chips>', function() {
1414
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index)"></md-chips>';
1515
var CHIP_SELECT_TEMPLATE =
1616
'<md-chips ng-model="items" md-on-select="selectChip($chip)"></md-chips>';
17+
var CHIP_READONLY_TEMPLATE =
18+
'<md-chips ng-model="items" readonly="isReadonly"></md-chips>';
1719
var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE =
1820
'<md-chips ng-model="items" readonly="true">' +
1921
' <md-autocomplete md-items="item in [\'hi\', \'ho\', \'he\']"></md-autocomplete>' +
@@ -254,12 +256,53 @@ describe('<md-chips>', function() {
254256
expect(scope.items[3].uppername).toBe('GRAPE');
255257
});
256258

257-
it('should not throw an error when using readonly with an autocomplete', function() {
258-
var element = buildChips(CHIP_READONLY_AUTOCOMPLETE_TEMPLATE);
259+
describe('when readonly', function() {
260+
var element, ctrl;
259261

260-
$timeout.flush();
262+
it("properly toggles the controller's readonly property", function() {
263+
element = buildChips(CHIP_READONLY_TEMPLATE);
264+
ctrl = element.controller('mdChips');
265+
266+
expect(ctrl.readonly).toBeFalsy();
267+
268+
scope.$apply('isReadonly = true');
269+
270+
expect(ctrl.readonly).toBeTruthy();
271+
});
272+
273+
it("properly toggles the wrapper's .md-readonly class", function() {
274+
element = buildChips(CHIP_READONLY_TEMPLATE);
275+
ctrl = element.controller('mdChips');
276+
277+
expect(element.find('md-chips-wrap')).not.toHaveClass('md-readonly');
278+
279+
scope.$apply('isReadonly = true');
280+
281+
expect(element.find('md-chips-wrap')).toHaveClass('md-readonly');
282+
});
283+
284+
it('is false with empty items should not hide the chips wrapper', function() {
285+
scope.isReadonly = false;
286+
scope.items = [];
287+
element = buildChips(CHIP_READONLY_TEMPLATE);
288+
289+
expect(element.find('md-chips-wrap').length).toBe(1);
290+
});
291+
292+
it('is true with empty items should not hide the chips wrapper', function() {
293+
scope.isReadonly = true;
294+
scope.items = [];
295+
element = buildChips(CHIP_READONLY_TEMPLATE);
296+
297+
expect(element.find('md-chips-wrap').length).toBe(1);
298+
});
299+
300+
it('is true should not throw an error when used with an autocomplete', function() {
301+
element = buildChips(CHIP_READONLY_AUTOCOMPLETE_TEMPLATE);
302+
$timeout.flush();
261303

262-
expect($exceptionHandler.errors).toEqual([]);
304+
expect($exceptionHandler.errors).toEqual([]);
305+
});
263306
});
264307

265308
it('should disallow duplicate object chips', function() {
@@ -451,6 +494,123 @@ describe('<md-chips>', function() {
451494
}));
452495
});
453496

497+
describe('md-max-chips', function() {
498+
499+
beforeEach(function() {
500+
// Clear default items to test the max chips functionality
501+
scope.items = [];
502+
});
503+
504+
it('should not add a new chip if the max-chips limit is reached', function () {
505+
var element = buildChips('<md-chips ng-model="items" md-max-chips="1"></md-chips>');
506+
var ctrl = element.controller('mdChips');
507+
508+
element.scope().$apply(function() {
509+
ctrl.chipBuffer = 'Test';
510+
simulateInputEnterKey(ctrl);
511+
});
512+
513+
expect(scope.items.length).toBe(1);
514+
515+
element.scope().$apply(function() {
516+
ctrl.chipBuffer = 'Test 2';
517+
simulateInputEnterKey(ctrl);
518+
});
519+
520+
expect(scope.items.length).not.toBe(2);
521+
});
522+
523+
it('should update the md-max-chips model validator for forms', function() {
524+
var template =
525+
'<form name="form">' +
526+
'<md-chips name="chips" ng-model="items" md-max-chips="1"></md-chips>' +
527+
'</form>';
528+
529+
var element = buildChips(template);
530+
var ctrl = element.find('md-chips').controller('mdChips');
531+
532+
element.scope().$apply(function() {
533+
ctrl.chipBuffer = 'Test';
534+
simulateInputEnterKey(ctrl);
535+
});
536+
537+
expect(scope.form.chips.$error['md-max-chips']).toBe(true);
538+
});
539+
540+
it('should not reset the buffer if the maximum is reached', function() {
541+
var element = buildChips('<md-chips ng-model="items" md-max-chips="1"></md-chips>');
542+
var ctrl = element.controller('mdChips');
543+
544+
element.scope().$apply(function() {
545+
ctrl.chipBuffer = 'Test';
546+
simulateInputEnterKey(ctrl);
547+
});
548+
549+
expect(scope.items.length).toBe(1);
550+
551+
element.scope().$apply(function() {
552+
ctrl.chipBuffer = 'Test 2';
553+
simulateInputEnterKey(ctrl);
554+
});
555+
556+
expect(ctrl.chipBuffer).toBe('Test 2');
557+
expect(scope.items.length).not.toBe(2);
558+
});
559+
});
560+
561+
describe('focus functionality', function() {
562+
var element, ctrl;
563+
564+
beforeEach(function() {
565+
element = buildChips(CHIP_SELECT_TEMPLATE);
566+
ctrl = element.controller('mdChips');
567+
document.body.appendChild(element[0]);
568+
});
569+
570+
afterEach(function() {
571+
element.remove();
572+
element = ctrl = null;
573+
});
574+
575+
it('should focus the chip when clicking / touching on the chip', function() {
576+
ctrl.focusChip = jasmine.createSpy('focusChipSpy');
577+
578+
var chips = getChipElements(element);
579+
expect(chips.length).toBe(3);
580+
581+
chips.children().eq(0).triggerHandler('click');
582+
583+
expect(ctrl.focusChip).toHaveBeenCalledTimes(1);
584+
});
585+
586+
it('should focus the chip through normal content focus', function() {
587+
scope.selectChip = jasmine.createSpy('focusChipSpy');
588+
var chips = getChipElements(element);
589+
expect(chips.length).toBe(3);
590+
591+
chips.children().eq(0).triggerHandler('focus');
592+
593+
expect(scope.selectChip).toHaveBeenCalledTimes(1);
594+
});
595+
596+
it('should blur the chip correctly', function() {
597+
var chips = getChipElements(element);
598+
expect(chips.length).toBe(3);
599+
600+
var chipContent = chips.children().eq(0);
601+
chipContent.triggerHandler('focus');
602+
603+
expect(ctrl.selectedChip).toBe(0);
604+
605+
chipContent.eq(0).triggerHandler('blur');
606+
607+
scope.$digest();
608+
609+
expect(ctrl.selectedChip).toBe(-1);
610+
});
611+
612+
});
613+
454614
describe('md-autocomplete', function() {
455615
var AUTOCOMPLETE_CHIPS_TEMPLATE = '\
456616
<md-chips ng-model="items">\

src/components/chips/js/chipDirective.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ function MdChip($mdTheming, $mdUtil) {
5252

5353
if (ctrl) angular.element(element[0].querySelector('.md-chip-content'))
5454
.on('blur', function () {
55-
ctrl.selectedChip = -1;
55+
ctrl.resetSelectedChip();
56+
ctrl.$scope.$applyAsync();
5657
});
5758
};
5859
}

src/components/chips/js/chipsDirective.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
<div class="md-chip-content"\
110110
tabindex="-1"\
111111
aria-hidden="true"\
112+
ng-click="!$mdChipsCtrl.readonly && $mdChipsCtrl.focusChip($index)"\
112113
ng-focus="!$mdChipsCtrl.readonly && $mdChipsCtrl.selectChip($index)"\
113114
md-chip-transclude="$mdChipsCtrl.chipContentsTemplate"></div>\
114115
<div ng-if="!$mdChipsCtrl.readonly"\

0 commit comments

Comments
 (0)