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

Commit

Permalink
feat(chips): add functionality to disable removing.
Browse files Browse the repository at this point in the history
- By default - chips are writable **and** deletable
- `readonly="true"`- chips are readonly and not deletable
- `readonly="true" md-removable="true"` - chips are readonly, and deletable
- `readonly="true" md-removable="false"` - chips are readonly, not deletable
- `md-removable="false"`- chips are writable, not deletable

Closes #5796. Fixes #3820.

Closes #5799
  • Loading branch information
devversion authored and ThomasBurleson committed Jun 23, 2016
1 parent 8758488 commit 4304e88
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 17 deletions.
6 changes: 5 additions & 1 deletion src/components/chips/chips.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,18 @@ $contact-chip-name-width: rem(12) !default;

&:not(.md-readonly) {
cursor: text;
}

&.md-removable {

md-chip:not(.md-readonly) {
md-chip {
@include rtl-prop(padding-right, padding-left, $chip-remove-padding-right);

._md-chip-content {
@include rtl-prop(padding-right, padding-left, rem(0.4));
}
}

}

md-chip {
Expand Down
128 changes: 128 additions & 0 deletions src/components/chips/chips.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ describe('<md-chips>', function() {
'<md-chips ng-model="items" readonly="true">' +
' <md-autocomplete md-items="item in [\'hi\', \'ho\', \'he\']"></md-autocomplete>' +
'</md-chips>';
var CHIP_NOT_REMOVABLE_TEMPLATE =
'<md-chips ng-model="items" readonly="true" md-removable="false"></md-chips>';

afterEach(function() {
attachedElements.forEach(function(element) {
Expand Down Expand Up @@ -200,6 +202,106 @@ describe('<md-chips>', function() {
expect(scope.selectChip).toHaveBeenCalled();
expect(scope.selectChip.calls.mostRecent().args[0]).toBe('Grape');
});

describe('when removable', function() {

it('should not append the input div when not removable and readonly is enabled', function() {
var element = buildChips(CHIP_NOT_REMOVABLE_TEMPLATE);
var wrap = element.children();
var controller = element.controller("mdChips");

expect(wrap.hasClass("md-removable")).toBe(false);
expect(controller.removable).toBe(false);

var containers = wrap[0].querySelectorAll(".md-chip-input-container");

expect(containers.length).toBe(0);

var removeContainer = wrap[0].querySelector('._md-chip-remove-container');
expect(removeContainer).not.toBeTruthy();
});

it('should not remove chip through the backspace/delete key when removable is set to false', inject(function($mdConstant) {
var element = buildChips(CHIP_NOT_REMOVABLE_TEMPLATE);
var wrap = element.find('md-chips-wrap');
var controller = element.controller("mdChips");
var chips = getChipElements(element);

expect(wrap.hasClass("md-removable")).toBe(false);
expect(controller.removable).toBe(false);

controller.selectChip(0);

wrap.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.BACKSPACE
});

var updatedChips = getChipElements(element);

expect(chips.length).toBe(updatedChips.length);
}));

it('should remove a chip by default through the backspace/delete key', inject(function($mdConstant) {
var element = buildChips(BASIC_CHIP_TEMPLATE);
var wrap = element.find('md-chips-wrap');
var controller = element.controller("mdChips");
var chips = getChipElements(element);

controller.selectChip(0);

wrap.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.BACKSPACE
});

var updatedChips = getChipElements(element);

expect(chips.length).not.toBe(updatedChips.length);
}));

it('should set removable to true by default', function() {
var element = buildChips(BASIC_CHIP_TEMPLATE);
var wrap = element.children();
var controller = element.controller('mdChips');

expect(wrap.hasClass('md-removable')).toBe(true);
// The controller variable is kept undefined by default, to allow us to difference between the default value
// and a user-provided value.
expect(controller.removable).toBe(undefined);

var containers = wrap[0].querySelectorAll("._md-chip-input-container");
expect(containers.length).not.toBe(0);

var removeContainer = wrap[0].querySelector('._md-chip-remove-container');
expect(removeContainer).toBeTruthy();
});

it('should append dynamically the remove button', function() {
var template = '<md-chips ng-model="items" readonly="true" md-removable="removable"></md-chips>';

scope.removable = false;

var element = buildChips(template);
var wrap = element.children();
var controller = element.controller("mdChips");

expect(wrap.hasClass("md-removable")).toBe(false);
expect(controller.removable).toBe(false);

var containers = wrap[0].querySelectorAll("._md-chip-remove-container");
expect(containers.length).toBe(0);

scope.$apply('removable = true');

expect(wrap.hasClass("md-removable")).toBe(true);
expect(controller.removable).toBe(true);

containers = wrap[0].querySelector("._md-chip-remove-container");
expect(containers).toBeTruthy();
});

});

describe('when readonly', function() {
var element, ctrl;
Expand Down Expand Up @@ -248,6 +350,32 @@ describe('<md-chips>', function() {

expect($exceptionHandler.errors).toEqual([]);
});

it('should disable removing when `md-removable` is not defined', function() {
element = buildChips(
'<md-chips ng-model="items" readonly="isReadonly" md-removable="isRemovable"></md-chips>'
);

var wrap = element.find('md-chips-wrap');
ctrl = element.controller('mdChips');

expect(element.find('md-chips-wrap')).not.toHaveClass('md-readonly');

scope.$apply('isReadonly = true');

expect(element.find('md-chips-wrap')).toHaveClass('md-readonly');

expect(ctrl.removable).toBeUndefined();

var removeContainer = wrap[0].querySelector('._md-chip-remove-container');
expect(removeContainer).toBeFalsy();

scope.$apply('isRemovable = true');

removeContainer = wrap[0].querySelector('._md-chip-remove-container');
expect(removeContainer).toBeTruthy();
});

});

it('should disallow duplicate object chips', function() {
Expand Down
21 changes: 15 additions & 6 deletions src/components/chips/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<h2 class="md-title">Use a custom chip template.</h2>

<form name="fruitForm">
<md-chips ng-model="ctrl.roFruitNames" name="fruitName" readonly="ctrl.readonly" md-max-chips="5">
<md-chips ng-model="ctrl.roFruitNames" name="fruitName" readonly="ctrl.readonly"
md-removable="ctrl.removable" md-max-chips="5">
<md-chip-template>
<strong>{{$chip}}</strong>
<em>(fruit)</em>
Expand All @@ -16,24 +17,26 @@ <h2 class="md-title">Use a custom chip template.</h2>
</div>
</form>


<br/>
<h2 class="md-title">Use the default chip template.</h2>

<md-chips ng-model="ctrl.fruitNames" readonly="ctrl.readonly"></md-chips>
<md-chips ng-model="ctrl.fruitNames" readonly="ctrl.readonly" md-removable="ctrl.removable"></md-chips>


<br/>
<h2 class="md-title">Make chips editable.</h2>

<md-chips ng-model="ctrl.editableFruitNames" readonly="ctrl.readonly" md-enable-chip-edit="true"></md-chips>
<md-chips ng-model="ctrl.editableFruitNames" readonly="ctrl.readonly" md-removable="ctrl.removable"
md-enable-chip-edit="true"></md-chips>

<br/>

<h2 class="md-title">Use Placeholders and override hint texts.</h2>

<md-chips
ng-model="ctrl.tags"
readonly="ctrl.readonly"
md-removable="ctrl.removable"
placeholder="Enter a tag"
delete-button-label="Remove Tag"
delete-hint="Press delete to remove tag"
Expand All @@ -44,7 +47,7 @@ <h2 class="md-title">Display an ordered set of objects as chips (with custom tem
<p>Note: the variables <code>$chip</code> and <code>$index</code> are available in custom chip templates.</p>

<md-chips class="custom-chips" ng-model="ctrl.vegObjs" readonly="ctrl.readonly"
md-transform-chip="ctrl.newVeg($chip)">
md-transform-chip="ctrl.newVeg($chip)" md-removable="ctrl.removable">
<md-chip-template>
<span>
<strong>[{{$index}}] {{$chip.name}}</strong>
Expand All @@ -58,6 +61,12 @@ <h2 class="md-title">Display an ordered set of objects as chips (with custom tem

<br/>
<md-checkbox ng-model="ctrl.readonly">Readonly</md-checkbox>

<md-checkbox ng-model="ctrl.removable">
Removable
<span ng-if="ctrl.removable === undefined">(Currently is <code>undefined</code>)</span>
</md-checkbox>
<p class="md-caption">
<b>Note</b>: When md-removable is undefined, readonly automatically sets md-removable to false.
</p>
</md-content>
</div>
11 changes: 7 additions & 4 deletions src/components/chips/demoBasicUsage/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@
}
}
}
&:not(.md-readonly) {
md-chip-template {
padding-right: 5px;
}
}

// Show custom padding for the custom delete button, which needs more space.
md-chips-wrap.md-removable {
md-chip md-chip-template {
padding-right: 5px;
}
}

}
10 changes: 8 additions & 2 deletions src/components/chips/js/chipsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout, $mdUtil) {
* after selecting a chip from the list.
* @type {boolean}
*/
this.useOnSelect = false;
}

/**
Expand Down Expand Up @@ -179,11 +178,16 @@ MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents){
* Returns true if a chip is currently being edited. False otherwise.
* @return {boolean}
*/
MdChipsCtrl.prototype.isEditingChip = function(){
MdChipsCtrl.prototype.isEditingChip = function() {
return !!this.$element[0].getElementsByClassName('_md-chip-editing').length;
};


MdChipsCtrl.prototype.isRemovable = function() {
return this.readonly ? this.removable :
angular.isDefined(this.removable) ? this.removable : true;
};

/**
* Handles the keydown event on the chip elements: backspace removes the selected chip, arrow
* keys switch which chips is active
Expand All @@ -198,6 +202,8 @@ MdChipsCtrl.prototype.chipKeydown = function (event) {
case this.$mdConstant.KEY_CODE.DELETE:
if (this.selectedChip < 0) return;
event.preventDefault();
// Cancel the delete action only after the event cancel. Otherwise the page will go back.
if (!this.isRemovable()) return;
this.removeAndSelectAdjacentChip(this.selectedChip);
break;
case this.$mdConstant.KEY_CODE.LEFT_ARROW:
Expand Down
14 changes: 10 additions & 4 deletions src/components/chips/js/chipsDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,12 @@
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
* @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input,
* displayed when there is at least one item in the list
* @param {boolean=} md-removable Enables or disables the deletion of chips through the
* removal icon or the Delete/Backspace key. Defaults to true.
* @param {boolean=} readonly Disables list manipulation (deleting or adding list items), hiding
* the input and delete buttons. If no `ng-model` is provided, the chips will automatically be
* marked as readonly.
* marked as readonly.<br/><br/>
* When `md-removable` is not defined, the `md-remove` behavior will be overwritten and disabled.
* @param {string=} md-enable-chip-edit Set this to "true" to enable editing of chip contents. The user can
* go into edit mode with pressing "space", "enter", or double clicking on the chip. Chip edit is only
* supported for chips with basic template.
Expand Down Expand Up @@ -145,7 +148,9 @@
var MD_CHIPS_TEMPLATE = '\
<md-chips-wrap\
ng-keydown="$mdChipsCtrl.chipKeydown($event)"\
ng-class="{ \'md-focused\': $mdChipsCtrl.hasFocus(), \'md-readonly\': !$mdChipsCtrl.ngModelCtrl || $mdChipsCtrl.readonly}"\
ng-class="{ \'md-focused\': $mdChipsCtrl.hasFocus(), \
\'md-readonly\': !$mdChipsCtrl.ngModelCtrl || $mdChipsCtrl.readonly,\
\'md-removable\': $mdChipsCtrl.isRemovable() }"\
class="md-chips">\
<md-chip ng-repeat="$chip in $mdChipsCtrl.items"\
index="{{$index}}"\
Expand All @@ -156,7 +161,7 @@
ng-click="!$mdChipsCtrl.readonly && $mdChipsCtrl.focusChip($index)"\
ng-focus="!$mdChipsCtrl.readonly && $mdChipsCtrl.selectChip($index)"\
md-chip-transclude="$mdChipsCtrl.chipContentsTemplate"></div>\
<div ng-if="!$mdChipsCtrl.readonly"\
<div ng-if="$mdChipsCtrl.isRemovable()"\
class="_md-chip-remove-container"\
md-chip-transclude="$mdChipsCtrl.chipRemoveTemplate"></div>\
</md-chip>\
Expand All @@ -183,7 +188,7 @@
var CHIP_REMOVE_TEMPLATE = '\
<button\
class="_md-chip-remove"\
ng-if="!$mdChipsCtrl.readonly"\
ng-if="$mdChipsCtrl.isRemovable()"\
ng-click="$mdChipsCtrl.removeChipAndFocusInput($$replacedScope.$index)"\
type="button"\
aria-hidden="true"\
Expand Down Expand Up @@ -218,6 +223,7 @@
compile: compile,
scope: {
readonly: '=readonly',
removable: '=mdRemovable',
placeholder: '@',
mdEnableChipEdit: '@',
secondaryPlaceholder: '@',
Expand Down

0 comments on commit 4304e88

Please sign in to comment.