Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

feat(templates): Support disabled options in select menu. #174

Merged
merged 1 commit into from
Sep 15, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bootstrap/choices.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<li class="ui-select-choices-group">
<div class="divider" ng-show="$select.isGrouped && $index > 0"></div>
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label dropdown-header">{{$group.name}}</div>
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this)}">
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}">
<a href="javascript:void(0)" class="ui-select-choices-row-inner"></a>
</div>
</li>
Expand Down
7 changes: 7 additions & 0 deletions src/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@
background-color: #428bca;
}

.ui-select-bootstrap .ui-select-choices-row.disabled>a,
.ui-select-bootstrap .ui-select-choices-row.active.disabled>a {
color: #777;
cursor: not-allowed;
background-color: #fff;
}

/* fix hide/show angular animation */
.ui-select-match.ng-hide-add,
.ui-select-search.ng-hide-add {
Expand Down
43 changes: 31 additions & 12 deletions src/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function
ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function
ctrl.multiple = false; // Initialized inside uiSelect directive link function
ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelect directive link function

ctrl.isEmpty = function() {
return angular.isUndefined(ctrl.selected) || ctrl.selected === null || ctrl.selected === '';
Expand Down Expand Up @@ -299,24 +300,40 @@
return ctrl.items.indexOf(itemScope[ctrl.itemProperty]) === ctrl.activeIndex;
};

ctrl.isDisabled = function(itemScope) {
var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
var isDisabled = false;
var item;

if (itemIndex >= 0 && !angular.isUndefined(ctrl.disableChoiceExpression)) {
item = ctrl.items[itemIndex];
isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression)); // force the boolean value
item._uiSelectChoiceDisabled = isDisabled; // store this for later reference
}

return isDisabled;
};

// When the user clicks on an item inside the dropdown
ctrl.select = function(item) {

var locals = {};
locals[ctrl.parserResult.itemName] = item;
if (!item._uiSelectChoiceDisabled) {
var locals = {};
locals[ctrl.parserResult.itemName] = item;

ctrl.onSelectCallback($scope, {
$item: item,
$model: ctrl.parserResult.modelMapper($scope, locals)
});
ctrl.onSelectCallback($scope, {
$item: item,
$model: ctrl.parserResult.modelMapper($scope, locals)
});

if(ctrl.multiple){
ctrl.selected.push(item);
ctrl.sizeSearchInput();
} else {
ctrl.selected = item;
if(ctrl.multiple){
ctrl.selected.push(item);
ctrl.sizeSearchInput();
} else {
ctrl.selected = item;
}
ctrl.close();
}
ctrl.close();
};

// Closes the dropdown
Expand Down Expand Up @@ -818,6 +835,8 @@

$select.parseRepeatAttr(attrs.repeat, groupByExp); //Result ready at $select.parserResult

$select.disableChoiceExpression = attrs.uiDisableChoice;

if(groupByExp) {
var groups = element.querySelectorAll('.ui-select-choices-group');
if (groups.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-group but got '{0}'.", groups.length);
Expand Down
2 changes: 1 addition & 1 deletion src/select2/choices.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<li class="ui-select-choices-group" ng-class="{'select2-result-with-children': $select.isGrouped}">
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label select2-result-label">{{$group.name}}</div>
<ul ng-class="{'select2-result-sub': $select.isGrouped, 'select2-result-single': !$select.isGrouped}">
<li class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.isActive(this)}">
<li class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.isActive(this), 'select2-disabled': $select.isDisabled(this)}">
<div class="select2-result-label ui-select-choices-row-inner"></div>
</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/selectize/choices.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="ui-select-choices-content selectize-dropdown-content">
<div class="ui-select-choices-group optgroup">
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label optgroup-header">{{$group.name}}</div>
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this)}">
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}">
<div class="option ui-select-choices-row-inner" data-selectable></div>
</div>
</div>
Expand Down
158 changes: 158 additions & 0 deletions test/select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,164 @@ describe('ui-select tests', function() {
expect(getMatchLabel(el)).toEqual('false');
});

describe('disabled options', function() {
function createUiSelect(attrs) {
var attrsDisabled = '';
if (attrs !== undefined) {
if (attrs.disabled !== undefined) {
attrsDisabled = ' ui-disable-choice="' + attrs.disabled + '"';
} else {
attrsDisabled = '';
}
}

return compileTemplate(
'<ui-select ng-model="selection.selected"> \
<ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
<ui-select-choices repeat="person in people | filter: $select.search"' + attrsDisabled + '> \
<div ng-bind-html="person.name | highlight: $select.search"></div> \
<div ng-bind-html="person.email | highlight: $select.search"></div> \
</ui-select-choices> \
</ui-select>'
);
}

function disablePerson(opts) {
opts = opts || {};

var key = opts.key || 'people',
disableAttr = opts.disableAttr || 'disabled',
disableBool = opts.disableBool === undefined ? true : opts.disableBool,
matchAttr = opts.match || 'name',
matchVal = opts.matchVal || 'Wladimir';

scope['_' + key] = angular.copy(scope[key]);
scope[key].map(function (model) {
if (model[matchAttr] == matchVal) {
model[disableAttr] = disableBool;
}
return model;
});
}

function resetScope(opts) {
opts = opts || {};
var key = opts.key || 'people';
scope[key] = angular.copy(scope['_' + key]);
}

describe('without disabling expression', function () {
beforeEach(function() {
disablePerson();
this.el = createUiSelect();
});

it('should not allow disabled options to be selected', function() {
clickItem(this.el, 'Wladimir');

expect(getMatchLabel(this.el)).toEqual('Wladimir');
});

it('should set a disabled class on the option', function() {
var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
var container = option.closest('.ui-select-choices-row');

expect(container.hasClass('disabled')).toBeFalsy();
});
});

describe('disable on truthy property', function () {
beforeEach(function() {
disablePerson({
disableAttr : 'inactive',
disableBool : true,
});
this.el = createUiSelect({
disabled: 'person.inactive'
});
});

it('should allow the user to define the selected option', function () {
expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe('person.inactive');
});

it('should not allow disabled options to be selected', function() {
clickItem(this.el, 'Wladimir');

expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
});

it('should set a disabled class on the option', function() {
var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
var container = option.closest('.ui-select-choices-row');

expect(container.hasClass('disabled')).toBeTruthy();
});
});

describe('disable on inverse property check', function () {
beforeEach(function() {
disablePerson({
disableAttr : 'active',
disableBool : false,
});
this.el = createUiSelect({
disabled: '!person.active'
});
});

it('should allow the user to define the selected option', function () {
expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe('!person.active');
});

it('should not allow disabled options to be selected', function() {
clickItem(this.el, 'Wladimir');

expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
});

it('should set a disabled class on the option', function() {
var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
var container = option.closest('.ui-select-choices-row');

expect(container.hasClass('disabled')).toBeTruthy();
});
});

describe('disable on expression', function () {
beforeEach(function() {
disablePerson({
disableAttr : 'status',
disableBool : 'inactive'
});
this.el = createUiSelect({
disabled: "person.status == 'inactive'"
});
});

it('should allow the user to define the selected option', function () {
expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe("person.status == 'inactive'");
});

it('should not allow disabled options to be selected', function() {
clickItem(this.el, 'Wladimir');

expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
});

it('should set a disabled class on the option', function() {
var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
var container = option.closest('.ui-select-choices-row');

expect(container.hasClass('disabled')).toBeTruthy();
});
});

afterEach(function() {
resetScope();
});
});

describe('choices group', function() {
function getGroupLabel(item) {
return item.parent('.ui-select-choices-group').find('.ui-select-choices-group-label');
Expand Down