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

Commit 713f7b6

Browse files
Ty PotterThomasBurleson
authored andcommitted
feat(chips): initial commit Supports basic list mutation (add, remove items).
Does not yet support child-input elements (renders its own input element by default). TODO/future work laid out in directive comments. Closes #2030. pending
1 parent f086de1 commit 713f7b6

9 files changed

Lines changed: 706 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
md-chips.md-THEME_NAME-theme {
2+
.md-chip {
3+
color: '{{background-contrast}}';
4+
background-color: '{{background-100}}';
5+
}
6+
}

src/components/chips/chips.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
(function () {
2+
'use strict';
3+
/**
4+
* @ngdoc module
5+
* @name material.components.chips
6+
*/
7+
/*
8+
* @see js folder for chips implementation
9+
*/
10+
angular.module('material.components.chips', [
11+
'material.core'
12+
]);
13+
})();

src/components/chips/chips.scss

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
$chip-font-size: 13px !default;
2+
$chip-height: 22px !default;
3+
$chip-button-padding: 2px !default;
4+
$chip-padding: 0 8px 0 8px !default;
5+
$chip-margin: 0 0 0 8px !default;
6+
7+
.md-chips {
8+
box-shadow: $whiteframe-shadow-z1;
9+
display: block;
10+
font-family: $font-family;
11+
font-size: $chip-font-size;
12+
padding: 8px;
13+
vertical-align: middle;
14+
15+
.md-chip {
16+
border-radius: $chip-height / 2;
17+
box-shadow: $whiteframe-shadow-z1;
18+
display: inline-block;
19+
height: $chip-height;
20+
margin: $chip-margin;
21+
line-height: $chip-height;
22+
padding: $chip-padding;
23+
transform: translate3d(0, 0, 0);
24+
25+
&:first-child {
26+
margin: 0;
27+
}
28+
29+
&.selected {
30+
transform: translate3d(0, -1px, 0);
31+
}
32+
33+
.md-button {
34+
padding: $chip-button-padding;
35+
}
36+
}
37+
38+
.md-chip-worker {
39+
display: inline-block;
40+
line-height: $chip-height + 3px; /* 3px are added to height of chip for...the shadow? */
41+
42+
&:not(:first-child) {
43+
margin: $chip-margin;
44+
}
45+
}
46+
47+
.md-chip-input {
48+
background-color:transparent;
49+
border-width: 0px;
50+
}
51+
}

src/components/chips/chips.spec.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
describe('<md-chips>', function() {
2+
3+
beforeEach(module('material.components.chips'));
4+
5+
function compile (str, scope) {
6+
var container;
7+
inject(function ($compile) {
8+
container = $compile(str)(scope);
9+
scope.$apply();
10+
});
11+
return container;
12+
}
13+
14+
function createScope () {
15+
var scope;
16+
var items = ['Apple', 'Banana', 'Orange'];
17+
inject(function ($rootScope) {
18+
scope = $rootScope.$new();
19+
scope.items = items;
20+
});
21+
return scope;
22+
}
23+
24+
function getChipElements(root) {
25+
return angular.element(root[0].querySelectorAll('div.md-chip'));
26+
}
27+
28+
describe('basic functionality', function () {
29+
it('should render a default input element', function() {
30+
var scope = createScope();
31+
var template = '<md-chips ng-model="items"></md-chips>';
32+
var element = compile(template, scope);
33+
var ctrl = element.controller('mdChips');
34+
35+
element.scope().$apply();
36+
var input = element.find('input');
37+
expect(input.length).toBe(1);
38+
expect(input).toHaveClass('md-chip-input');
39+
});
40+
41+
it('should render a list of chips', function() {
42+
var scope = createScope();
43+
var template = '<md-chips ng-model="items"></md-chips>';
44+
var element = compile(template, scope);
45+
var ctrl = element.controller('mdChips');
46+
47+
element.scope().$apply();
48+
var chips = getChipElements(element);
49+
expect(chips.length).toBe(3);
50+
expect(chips[0].innerHTML).toContain('Apple');
51+
expect(chips[1].innerHTML).toContain('Banana');
52+
expect(chips[2].innerHTML).toContain('Orange');
53+
});
54+
55+
it('should render a user-provided chip template', function() {
56+
var scope = createScope();
57+
var template =
58+
'<md-chips ng-model="items">' +
59+
' <md-chip class="mychiptemplate"></md-chip>' +
60+
'</md-chips>';
61+
var element = compile(template, scope);
62+
var ctrl = element.controller('mdChips');
63+
64+
element.scope().$apply();
65+
var chip = element.find('md-chip');
66+
expect(chip).toHaveClass('mychiptemplate');
67+
});
68+
69+
it('should add a chip', function() {
70+
var scope = createScope();
71+
var template = '<md-chips ng-model="items"></md-chips>';
72+
var element = compile(template, scope);
73+
var ctrl = element.controller('mdChips');
74+
75+
element.scope().$apply();
76+
ctrl.chipBuffer = 'Grape';
77+
element.scope().$apply();
78+
79+
ctrl.appendChipBuffer();
80+
element.scope().$apply();
81+
82+
var chips = getChipElements(element);
83+
expect(chips.length).toBe(4);
84+
expect(chips[0].innerHTML).toContain('Apple');
85+
expect(chips[1].innerHTML).toContain('Banana');
86+
expect(chips[2].innerHTML).toContain('Orange');
87+
expect(chips[3].innerHTML).toContain('Grape');
88+
});
89+
90+
it('should remove a chip', function() {
91+
var scope = createScope();
92+
var template = '<md-chips ng-model="items"></md-chips>';
93+
var element = compile(template, scope);
94+
var ctrl = element.controller('mdChips');
95+
96+
element.scope().$apply();
97+
98+
// Remove "Banana"
99+
ctrl.removeChip(1);
100+
element.scope().$apply();
101+
102+
var chips = getChipElements(element);
103+
expect(chips.length).toBe(2);
104+
expect(chips[0].innerHTML).toContain('Apple');
105+
expect(chips[1].innerHTML).toContain('Orange');
106+
});
107+
});
108+
109+
describe('<md-chip-remove>', function() {
110+
it('should remove a chip', function() {
111+
var scope = createScope();
112+
var template = '<md-chips ng-model="items"></md-chips>';
113+
var element = compile(template, scope);
114+
var ctrl = element.controller('mdChips');
115+
element.scope().$apply();
116+
117+
var chips = getChipElements(element);
118+
expect(chips.length).toBe(3);
119+
// Remove 'Banana'
120+
var db = angular.element(chips[1]).find('md-button');
121+
db[0].click();
122+
element.scope().$apply();
123+
124+
chips = getChipElements(element);
125+
expect(chips.length).toBe(2);
126+
127+
// Remove 'Orange'
128+
db = angular.element(chips[1]).find('md-button');
129+
db[0].click();
130+
element.scope().$apply();
131+
132+
chips = getChipElements(element);
133+
expect(chips.length).toBe(1);
134+
});
135+
});
136+
137+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<div ng-app="inputBasicDemo" ng-controller="DemoCtrl as ctrl" layout="column">
2+
3+
<md-content class="md-padding" layout="column">
4+
<p>
5+
Display a readonly list of strings as chips, using a custom chip template.
6+
</p>
7+
8+
<md-chips ng-model="ctrl.roFruitNames"
9+
readonly="true">
10+
<md-chip><strong>{{$chip}}</strong></md-chip>
11+
</md-chips>
12+
13+
<p>
14+
Use the default chip template.
15+
</p>
16+
17+
<md-chips
18+
ng-model="ctrl.fruitNames"
19+
readonly="ctrl.readonly"></md-chips>
20+
21+
<p>
22+
Use Placeholders.
23+
</p>
24+
25+
<md-chips
26+
ng-model="ctrl.newFruitNames"
27+
readonly="ctrl.readonly"
28+
placeholder="Enter a fruit"
29+
secondary-placeholder="+Fruit"></md-chips>
30+
31+
<p>
32+
Display a list of objects as chips.
33+
</p>
34+
35+
<md-chips ng-model="ctrl.vegObjs"
36+
readonly="ctrl.readonly"
37+
md-chip-append="ctrl.newVeg($chip)">
38+
<md-chip><span>{{$chip.name}}:{{$chip.type}}</span></md-chip>
39+
</md-chips>
40+
41+
<br/>
42+
<md-checkbox ng-model="ctrl.readonly">Readonly</md-checkbox>
43+
44+
</md-content>
45+
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
(function () {
2+
angular
3+
.module('contactchipsDemo', ['ngMaterial'])
4+
.controller('DemoCtrl', DemoCtrl);
5+
6+
function DemoCtrl ($timeout, $q) {
7+
var self = this;
8+
9+
self.readonly = false;
10+
11+
// Lists of fruit names and Vegetable objects
12+
self.fruitNames = ['Apple', 'Banana', 'Orange'];
13+
self.roFruitNames = angular.copy(self.fruitNames);
14+
self.newFruitNames = [];
15+
self.vegObjs = [
16+
{
17+
'name' : 'Broccoli',
18+
'type' : 'cruciferous'
19+
},
20+
{
21+
'name' : 'Cabbage',
22+
'type' : 'cruciferous'
23+
},
24+
{
25+
'name' : 'Carrot',
26+
'type' : 'root'
27+
}
28+
];
29+
30+
self.newVeg = function(chip) {
31+
return {
32+
name: chip,
33+
type: 'unknown'
34+
};
35+
};
36+
}
37+
})();
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
(function () {
2+
'use strict';
3+
angular
4+
.module('material.components.chips')
5+
.directive('mdChipRemove', MdChipRemove);
6+
7+
/**
8+
* @ngdoc directive
9+
* @name mdChipRemove
10+
* @module material.components.chips
11+
*
12+
* @description
13+
* `<element md-chip-remove>`
14+
* Identifies an element within an <md-chip> as the delete button. This
15+
* directive binds to that element's click event and removes the chip.
16+
*
17+
* @usage
18+
* <hljs lang="html">
19+
* <md-chip>{{$chip}}<button md-chip-remove>x</button></md-chip>
20+
* </hljs>
21+
*/
22+
23+
function MdChipRemove () {
24+
return {
25+
restrict: 'A',
26+
require: ['^mdChips'],
27+
link: function postLink(scope, element, attrs, controllers) {
28+
var mdChipsCtrl = controllers[0];
29+
element.on('click', removeItemListener(mdChipsCtrl, scope));
30+
},
31+
scope: false
32+
};
33+
34+
function removeItemListener(chipsCtrl, scope) {
35+
return function() {
36+
scope.$apply(function() {
37+
chipsCtrl.removeChip(scope.$index);
38+
});
39+
};
40+
}
41+
}
42+
})();

0 commit comments

Comments
 (0)