Skip to content

Commit 9184457

Browse files
jeff-phillips-18dgutride
authored andcommitted
Add pfSelect component (#381)
1 parent bc090c9 commit 9184457

File tree

7 files changed

+265
-0
lines changed

7 files changed

+265
-0
lines changed

Gruntfile.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ module.exports = function (grunt) {
193193
src: ['modals/**/*.html'],
194194
dest: 'templates/modals.js'
195195
},
196+
'patternfly.select': {
197+
cwd: 'src/',
198+
src: ['select/**/*.html'],
199+
dest: 'templates/select.js'
200+
},
196201
'patternfly.sort': {
197202
cwd: 'src/',
198203
src: ['sort/**/*.html'],

src/patternfly.module.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ angular.module('patternfly', [
1212
'patternfly.modals',
1313
'patternfly.navigation',
1414
'patternfly.notification',
15+
'patternfly.select',
1516
'patternfly.sort',
1617
'patternfly.toolbars',
1718
'patternfly.utils',

src/select/examples/select.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @ngdoc directive
3+
* @name patternfly.select.component:pfSelect
4+
* @restrict E
5+
*
6+
* @param {string} ngModel Model binding using the {@link https://docs.angularjs.org/api/ng/type/ngModel.NgModelController/ NgModelController} is mandatory.
7+
* @param {string=} ngOptions The `{@link https://docs.angularjs.org/api/ng/directive/select/ ngOptions}` attribute can be used to dynamically generate a list of `<option>` elements
8+
*
9+
* @description
10+
* The pfSelect component provides a wrapper for the angular ui bootstrap dropdown container allowing for use of ng-model and ng-options
11+
*
12+
* @example
13+
<example module="patternfly.select">
14+
<file name="index.html">
15+
<div ng-controller="SelectDemoCtrl">
16+
<form class="form-horizontal">
17+
<div class="form-group">
18+
<label class="col-sm-2 control-label">Preferred pet:</label>
19+
<div class="col-sm-10">
20+
<pf-select selected="pet" empty-value="{{noPet}}" options="pets"></pf-select>
21+
</div>
22+
</div>
23+
<div class="form-group">
24+
<label class="col-sm-2 control-label">Preferred fruit:</label>
25+
<div class="col-sm-10">
26+
<pf-select selected="fruit" options="fruits" display-field="title"></pf-select>
27+
</div>
28+
</div>
29+
<div class="form-group">
30+
<label class="col-sm-2 control-label">Preferred drink:</label>
31+
<div class="col-sm-10">
32+
<pf-select selected="drink" empty-value="{{noDrink}}" options="drinks" display-field="name"></pf-select>
33+
</div>
34+
</div>
35+
</form>
36+
<p>Your preferred pet is {{pet || noPet}}.</p>
37+
<p>Your preferred drink is {{fruit.name}}.</p>
38+
<p>Your preferred drink is {{drink ? drink.name : noDrink}}.</p>
39+
</div>
40+
</file>
41+
<file name="script.js">
42+
angular.module( 'patternfly.select' ).controller( 'SelectDemoCtrl', function( $scope ) {
43+
$scope.pets = ['Dog', 'Cat', 'Chicken'];
44+
$scope.noPet = "No pet selected";
45+
46+
$scope.fruits = [
47+
{ id: 1, name:'orange', title: 'Oranges - fresh from Florida'},
48+
{ id: 2, name:'apple', title: 'Apples - Macintosh, great for pies.'},
49+
{ id: 3, name:'banana', title: 'Bananas - you will go ape for them!' }
50+
];
51+
$scope.fruit = $scope.fruits[0];
52+
53+
$scope.drinks = [
54+
{ id: 1, name:'tea'},
55+
{ id: 2, name:'coffee'},
56+
{ id: 3, name:'water'},
57+
{ id: 4, name:'wine'},
58+
{ id: 5, name:'beer'}
59+
];
60+
$scope.drink = $scope.drinks[0];
61+
$scope.noDrink = "No drink selected";
62+
});
63+
</file>
64+
</example>
65+
*/

src/select/select.component.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
angular.module('patternfly.select').component('pfSelect', {
2+
3+
bindings: {
4+
selected: '=',
5+
options: '<',
6+
displayField: '@',
7+
emptyValue: '@',
8+
onSelect: '<'
9+
},
10+
templateUrl: 'select/select.html',
11+
controller: function () {
12+
'use strict';
13+
14+
var ctrl = this;
15+
16+
ctrl.$onInit = function () {
17+
angular.extend(ctrl, {
18+
showEmpty: angular.isDefined(ctrl.emptyValue),
19+
getDisplayValue: getDisplayValue,
20+
selectItem: selectItem
21+
});
22+
};
23+
24+
function getDisplayValue (item) {
25+
var value;
26+
27+
if (item !== ctrl.emptyValue && angular.isString(ctrl.displayField)) {
28+
value = item[ctrl.displayField];
29+
} else {
30+
value = item;
31+
}
32+
33+
return value;
34+
}
35+
36+
function selectItem (item) {
37+
ctrl.selected = item;
38+
if (angular.isFunction(ctrl.onSelect)) {
39+
ctrl.onSelect(item);
40+
}
41+
}
42+
}
43+
});

src/select/select.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div uib-dropdown class="btn-group">
2+
<button uib-dropdown-toggle type="button" class="btn btn-default">
3+
{{$ctrl.getDisplayValue($ctrl.selected || $ctrl.emptyValue)}}
4+
<span class="caret"></span>
5+
</button>
6+
<ul uib-dropdown-menu>
7+
<li ng-if="$ctrl.emptyValue" ng-class="{'selected': !$ctrl.selected}">
8+
<a href="javascript:void(0);" role="menuitem" tabindex="-1" ng-click="$ctrl.selectItem()">
9+
{{$ctrl.emptyValue}}
10+
</a>
11+
</li>
12+
<li ng-repeat="item in $ctrl.options" ng-class="{'selected': item === $ctrl.selected}">
13+
<a href="javascript:void(0);" role="menuitem" tabindex="-1" ng-click="$ctrl.selectItem(item)">
14+
{{$ctrl.getDisplayValue(item)}}
15+
</a>
16+
</li>
17+
</ul>
18+
</div>

src/select/select.module.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @name patternfly card
3+
*
4+
* @description
5+
* Select module for patternfly.
6+
*
7+
*/
8+
angular.module('patternfly.select', ['ui.bootstrap']);

test/select/select.spec.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
describe('Component: pfSelect', function () {
2+
var $scope;
3+
var $compile;
4+
var element;
5+
6+
// load the controller's module
7+
beforeEach(function () {
8+
module('patternfly.select', 'select/select.html');
9+
});
10+
11+
beforeEach(inject(function (_$compile_, _$rootScope_) {
12+
$compile = _$compile_;
13+
$scope = _$rootScope_;
14+
}));
15+
16+
var compileHTML = function (markup, scope) {
17+
element = angular.element(markup);
18+
$compile(element)(scope);
19+
20+
scope.$digest();
21+
};
22+
23+
beforeEach(function () {
24+
$scope.items = [
25+
{
26+
id: 1,
27+
title: 'A'
28+
},
29+
{
30+
id: 2,
31+
title: 'B'
32+
},
33+
{
34+
id: 3,
35+
title: 'C'
36+
},
37+
{
38+
id: 4,
39+
title: 'D'
40+
}
41+
];
42+
43+
$scope.selected = $scope.items[1];
44+
45+
var htmlTmp = '<pf-select selected="selected" options="items" display-field="title"></pf-select>';
46+
47+
compileHTML(htmlTmp, $scope);
48+
});
49+
50+
it('should have correct number of options', function () {
51+
var menuItems = element.find('.dropdown-menu li');
52+
expect(menuItems.length).toBe(4);
53+
});
54+
55+
it('should have the correct item selected', function () {
56+
var results = element.find('.dropdown-toggle');
57+
expect(results.length).toBe(1);
58+
expect(results.html().trim().slice(0, $scope.items[1].title.length)).toBe($scope.items[1].title);
59+
});
60+
61+
it('should update the selected item when one is clicked', function () {
62+
var menuItems = element.find('.dropdown-menu li > a');
63+
expect(menuItems.length).toBe(4);
64+
65+
eventFire(menuItems[2], 'click');
66+
$scope.$digest();
67+
68+
expect($scope.selected.id).toBe(3);
69+
var results = element.find('.dropdown-toggle');
70+
expect(results.length).toBe(1);
71+
expect(results.html().trim().slice(0, $scope.items[1].title.length)).toBe($scope.items[2].title);
72+
});
73+
74+
it('should add a default item when empty value is given', function () {
75+
var htmlTmp = '<pf-select selected="selected" options="items" display-field="title" empty-value="nothing"></pf-select>';
76+
77+
compileHTML(htmlTmp, $scope);
78+
79+
var menuItems = element.find('.dropdown-menu li');
80+
expect(menuItems.length).toBe(5);
81+
});
82+
83+
it('should show the default when no selection is given', function () {
84+
85+
$scope.selected = null;
86+
var htmlTmp = '<pf-select selected="selected" options="items" display-field="title" empty-value="nothing"></pf-select>';
87+
88+
compileHTML(htmlTmp, $scope);
89+
90+
var menuItems = element.find('.dropdown-menu li');
91+
expect(menuItems.length).toBe(5);
92+
93+
var results = element.find('.dropdown-toggle');
94+
expect(results.length).toBe(1);
95+
expect(results.html().trim().slice(0, "nothing".length)).toBe("nothing");
96+
});
97+
98+
it('should call the onSelect function on selection', function () {
99+
100+
var onSelectCalled = false;
101+
var selectedItem = null;
102+
$scope.onSelect = function(item) {
103+
onSelectCalled = true;
104+
selectedItem = item;
105+
};
106+
107+
$scope.selected = null;
108+
var htmlTmp = '<pf-select selected="selected" options="items" display-field="title" empty-value="nothing" on-select="onSelect"></pf-select>';
109+
110+
compileHTML(htmlTmp, $scope);
111+
112+
var menuItems = element.find('.dropdown-menu li > a');
113+
expect(menuItems.length).toBe(5);
114+
115+
expect(onSelectCalled).toBe(false);
116+
expect(selectedItem).toBe(null);
117+
118+
eventFire(menuItems[2], 'click');
119+
$scope.$digest();
120+
121+
expect(onSelectCalled).toBe(true);
122+
expect(selectedItem.id).toBe(2);
123+
});
124+
125+
});

0 commit comments

Comments
 (0)