mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
Merge pull request #116 from joefiorini/multi-select
Implements multi-select-list component
This commit is contained in:
commit
48a26a633c
@ -1 +1 @@
|
||||
import 'tower/debug';
|
||||
import 'tower/shared/multi-select-list/main.js';
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name forms.function:Organizations
|
||||
* @name forms.function:CustomInventory
|
||||
* @description This form is for adding/editing an organization
|
||||
*/
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:Schedules
|
||||
* @name helpers.function:ConfigureTower
|
||||
* @description
|
||||
* Schedules Helper
|
||||
*
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:Schedules
|
||||
* @name helpers.function:CustomInventory
|
||||
* @description
|
||||
* Schedules Helper
|
||||
*
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:Schedules
|
||||
* @name helpers.function:Survey
|
||||
* @description
|
||||
* Schedules Helper
|
||||
*
|
||||
|
||||
@ -40,8 +40,8 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
// hdr: <lookup dialog header>
|
||||
//
|
||||
// Inject into a custom element using options.id: <element id attribute value>
|
||||
// Control breadcrumb creation with options.breadCrumbs: <true | false>
|
||||
//
|
||||
// Control breadcrumb creation with options.breadCrumbs: <true | false>
|
||||
var element;
|
||||
|
||||
if (options.id) {
|
||||
@ -115,6 +115,31 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
}
|
||||
};
|
||||
|
||||
if (list.multiSelect) {
|
||||
var cleanupSelectionChanged =
|
||||
this.scope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
this.scope.selectedItems = selection.selectedItems;
|
||||
|
||||
// Track the selected state of each item
|
||||
// for changing the row class based on the
|
||||
// selection
|
||||
//
|
||||
selection.selectedItems.forEach(function(item) {
|
||||
item.isSelected = true;
|
||||
});
|
||||
|
||||
selection.deselectedItems.forEach(function(item) {
|
||||
item.isSelected = false;
|
||||
});
|
||||
|
||||
}.bind(this));
|
||||
|
||||
this.scope.$on('$destroy', function() {
|
||||
cleanupSelectionChanged();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$compile(element)(this.scope);
|
||||
|
||||
// Reset the scope to prevent displaying old data from our last visit to this list
|
||||
@ -294,34 +319,62 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
html += "<div class=\"list-table-container\"";
|
||||
html += (list.awCustomScroll) ? " aw-custom-scroll " : "";
|
||||
html += ">\n";
|
||||
html += "<table id=\"" + list.name + "_table\" ";
|
||||
html += "class=\"table table-condensed";
|
||||
html += (list['class']) ? " " + list['class'] : "";
|
||||
html += (options.mode !== 'summary' && options.mode !== 'edit' && (options.mode === 'lookup' || options.id)) ?
|
||||
' table-hover-inverse' : '';
|
||||
html += (list.hover) ? ' table-hover' : '';
|
||||
html += (options.mode === 'summary') ? ' table-summary' : '';
|
||||
html += "\" ";
|
||||
html += ">\n";
|
||||
|
||||
function buildTable() {
|
||||
var extraClasses = list['class'];
|
||||
var multiSelect = list.multiSelect ? 'multi-select-list' : null;
|
||||
|
||||
if (options.mode !== 'summary' && options.mode !== 'edit' && (options.mode === 'lookup' || options.id)) {
|
||||
extraClasses += ' table-hover-inverse';
|
||||
}
|
||||
|
||||
if (list.hover) {
|
||||
extraClasses += ' table-hover';
|
||||
}
|
||||
|
||||
if (options.mode === 'summary') {
|
||||
extraClasses += ' table-summary';
|
||||
}
|
||||
|
||||
return $('<table>')
|
||||
.attr('id', list.name + '_table')
|
||||
.addClass('table table-condensed')
|
||||
.addClass(extraClasses)
|
||||
.attr('multi-select-list', multiSelect);
|
||||
|
||||
}
|
||||
|
||||
var table = buildTable();
|
||||
var innerTable = '';
|
||||
|
||||
if (!options.skipTableHead) {
|
||||
html += this.buildHeader(options);
|
||||
innerTable += this.buildHeader(options);
|
||||
}
|
||||
|
||||
// table body
|
||||
html += "<tbody>\n";
|
||||
html += "<tr ng-class=\"" + list.iterator;
|
||||
html += (options.mode === 'lookup' || options.mode === 'select') ? ".success_class" : ".active_class";
|
||||
html += "\" ";
|
||||
html += "id=\"{{ " + list.iterator + ".id }}\" ";
|
||||
html += "class=\"" + list.iterator + "_class\" ";
|
||||
html += "ng-repeat=\"" + list.iterator + " in " + list.name;
|
||||
html += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
|
||||
html += (list.filterBy) ? " | filter: " + list.filterBy : "";
|
||||
html += "\">\n";
|
||||
if (list.index) {
|
||||
html += "<td class=\"index-column hidden-xs\">{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.</td>\n";
|
||||
innerTable += "<tbody>\n";
|
||||
innerTable += "<tr ng-class=\"[" + list.iterator;
|
||||
innerTable += (options.mode === 'lookup' || options.mode === 'select') ? ".success_class" : ".active_class";
|
||||
|
||||
if (list.multiSelect) {
|
||||
innerTable += ", " + list.iterator + ".isSelected ? 'is-selected-row' : ''";
|
||||
}
|
||||
innerTable += "]\" ";
|
||||
innerTable += "id=\"{{ " + list.iterator + ".id }}\" ";
|
||||
innerTable += "class=\"" + list.iterator + "_class\" ";
|
||||
innerTable += "ng-repeat=\"" + list.iterator + " in " + list.name;
|
||||
innerTable += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
|
||||
innerTable += (list.filterBy) ? " | filter: " + list.filterBy : "";
|
||||
innerTable += "\">\n";
|
||||
|
||||
if (list.index) {
|
||||
innerTable += "<td class=\"index-column hidden-xs\">{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.</td>\n";
|
||||
}
|
||||
|
||||
if (list.multiSelect) {
|
||||
innerTable += '<td class="col-xs-1 select-column"><select-list-item item=\"' + list.iterator + '\"></select-list-item></td>';
|
||||
}
|
||||
|
||||
cnt = 2;
|
||||
base = (list.base) ? list.base : list.name;
|
||||
base = base.replace(/^\//, '');
|
||||
@ -329,7 +382,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
cnt++;
|
||||
if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) &&
|
||||
!(options.mode === 'lookup' && list.fields[fld].excludeModal === true)) {
|
||||
html += Column({
|
||||
innerTable += Column({
|
||||
list: list,
|
||||
fld: fld,
|
||||
options: options,
|
||||
@ -340,12 +393,12 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
|
||||
if (options.mode === 'select' || options.mode === 'lookup') {
|
||||
if(options.input_type==="radio"){ //added by JT so that lookup forms can be either radio inputs or check box inputs
|
||||
html += "<td><input type=\"radio\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
innerTable += "<td><input type=\"radio\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator + "(" + list.iterator + ".id, true)\" ng-value=\"1\" " +
|
||||
"ng-false-value=\"0\" id=\"check_{{" + list.iterator + ".id}}\" /></td>";
|
||||
}
|
||||
else { // its assumed that options.input_type = checkbox
|
||||
html += "<td><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
innerTable += "<td><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator + "(" + list.iterator + ".id, true)\" ng-true-value=\"1\" " +
|
||||
"ng-false-value=\"0\" id=\"check_{{" + list.iterator + ".id}}\" /></td>";
|
||||
}
|
||||
@ -353,12 +406,12 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
|
||||
// Row level actions
|
||||
|
||||
html += "<td class=\"actions\">";
|
||||
innerTable += "<td class=\"actions\">";
|
||||
|
||||
for (field_action in list.fieldActions) {
|
||||
if (field_action !== 'columnClass') {
|
||||
if (list.fieldActions[field_action].type && list.fieldActions[field_action].type === 'DropDown') {
|
||||
html += DropDown({
|
||||
innerTable += DropDown({
|
||||
list: list,
|
||||
fld: field_action,
|
||||
options: options,
|
||||
@ -368,53 +421,56 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
});
|
||||
} else {
|
||||
fAction = list.fieldActions[field_action];
|
||||
html += "<a id=\"";
|
||||
html += (fAction.id) ? fAction.id : field_action + "-action";
|
||||
html += "\" ";
|
||||
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
||||
html += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
|
||||
html += (field_action === 'cancel') ? "class=\"cancel red-txt\" " : "";
|
||||
html += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
|
||||
html += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
|
||||
html += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
|
||||
innerTable += "<a id=\"";
|
||||
innerTable += (fAction.id) ? fAction.id : field_action + "-action";
|
||||
innerTable += "\" ";
|
||||
innerTable += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
||||
innerTable += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
|
||||
innerTable += (field_action === 'cancel') ? "class=\"cancel red-txt\" " : "";
|
||||
innerTable += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
|
||||
innerTable += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
|
||||
innerTable += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
|
||||
for (itm in fAction) {
|
||||
if (itm !== 'ngHref' && itm !== 'href' && itm !== 'label' && itm !== 'icon' && itm !== 'class' &&
|
||||
itm !== 'iconClass' && itm !== "dataPlacement" && itm !== "awPopOver" &&
|
||||
itm !== "dataTitle") {
|
||||
html += Attr(fAction, itm);
|
||||
innerTable += Attr(fAction, itm);
|
||||
}
|
||||
}
|
||||
html += ">";
|
||||
innerTable += ">";
|
||||
if (fAction.iconClass) {
|
||||
html += "<i class=\"" + fAction.iconClass + "\"></i>";
|
||||
innerTable += "<i class=\"" + fAction.iconClass + "\"></i>";
|
||||
} else {
|
||||
html += SelectIcon({
|
||||
innerTable += SelectIcon({
|
||||
action: field_action
|
||||
});
|
||||
}
|
||||
//html += (fAction.label) ? "<span class=\"list-action-label\"> " + list.fieldActions[field_action].label +
|
||||
// "</span>" : "";
|
||||
html += "</a>";
|
||||
innerTable += "</a>";
|
||||
}
|
||||
}
|
||||
}
|
||||
html += "</td>\n";
|
||||
innerTable += "</td>\n";
|
||||
}
|
||||
html += "</tr>\n";
|
||||
innerTable += "</tr>\n";
|
||||
|
||||
// Message for when a collection is empty
|
||||
html += "<tr class=\"loading-info\" ng-show=\"" + list.iterator + "Loading == false && " + list.name + ".length == 0\">\n";
|
||||
html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">No records matched your search.</div></td>\n";
|
||||
html += "</tr>\n";
|
||||
innerTable += "<tr class=\"loading-info\" ng-show=\"" + list.iterator + "Loading == false && " + list.name + ".length == 0\">\n";
|
||||
innerTable += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">No records matched your search.</div></td>\n";
|
||||
innerTable += "</tr>\n";
|
||||
|
||||
// Message for loading
|
||||
html += "<tr class=\"loading-info\" ng-show=\"" + list.iterator + "Loading == true\">\n";
|
||||
html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">Loading...</div></td>\n";
|
||||
html += "</tr>\n";
|
||||
innerTable += "<tr class=\"loading-info\" ng-show=\"" + list.iterator + "Loading == true\">\n";
|
||||
innerTable += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">Loading...</div></td>\n";
|
||||
innerTable += "</tr>\n";
|
||||
|
||||
// End List
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
innerTable += "</tbody>\n";
|
||||
|
||||
table.html(innerTable);
|
||||
html += table.prop('outerHTML');
|
||||
|
||||
html += "</div><!-- table container -->\n";
|
||||
|
||||
if (options.mode === 'select' && (options.selectButton === undefined || options.selectButton)) {
|
||||
@ -447,6 +503,15 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
var list = this.list,
|
||||
fld, html;
|
||||
|
||||
function buildSelectAll() {
|
||||
return $('<th>')
|
||||
.addClass('col-xs-1 select-column')
|
||||
.append(
|
||||
$('<select-all>')
|
||||
.attr('selections-empty', 'selectedItems.length === 0')
|
||||
.attr('items-length', list.name + '.length'));
|
||||
}
|
||||
|
||||
if (options === undefined) {
|
||||
options = this.options;
|
||||
}
|
||||
@ -456,6 +521,11 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
if (list.index) {
|
||||
html += "<th class=\"col-lg-1 col-md-1 col-sm-2 hidden-xs\">#</th>\n";
|
||||
}
|
||||
|
||||
if (list.multiSelect) {
|
||||
html += buildSelectAll().prop('outerHTML');
|
||||
}
|
||||
|
||||
for (fld in list.fields) {
|
||||
if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) &&
|
||||
!(options.mode === 'lookup' && list.fields[fld].excludeModal === true)) {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import generateList from './list-generator.factory';
|
||||
import toolbarButton from './toolbar-button.directive';
|
||||
import generatorHelpers from 'tower/shared/generator-helpers';
|
||||
import multiSelectList from 'tower/shared/multi-select-list/main';
|
||||
|
||||
export default
|
||||
angular.module('listGenerator', [generatorHelpers.name])
|
||||
angular.module('listGenerator', [generatorHelpers.name, multiSelectList.name])
|
||||
.factory('generateList', generateList)
|
||||
.directive('toolbarButton', toolbarButton);
|
||||
|
||||
9
awx/ui/static/js/shared/multi-select-list/main.js
Normal file
9
awx/ui/static/js/shared/multi-select-list/main.js
Normal file
@ -0,0 +1,9 @@
|
||||
import multiSelect from './multi-select-list.directive';
|
||||
import selectAll from './select-all.directive';
|
||||
import selectListItem from './select-list-item.directive';
|
||||
|
||||
export default
|
||||
angular.module('multiSelectList', [])
|
||||
.directive('multiSelectList', multiSelect)
|
||||
.directive('selectAll', selectAll)
|
||||
.directive('selectListItem', selectListItem);
|
||||
@ -0,0 +1,192 @@
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* `multiSelectList` controller provides the API for the {@link multiSelectList.directive:multiSelectList `multiSelectList`} directive. The controller contains methods for selecting/deselecting items, controlling the extended selection, registering items to be selectable and emitting an event on the directive's `$scope`.
|
||||
*
|
||||
*/
|
||||
export default ['$scope',
|
||||
function ($scope) {
|
||||
$scope.items = [];
|
||||
$scope.selection = {
|
||||
isExtended: false,
|
||||
selectedItems: [],
|
||||
deselectedItems: []
|
||||
};
|
||||
|
||||
// Makes $scope.selection.length an alias for $scope.selectedItems.length
|
||||
Object.defineProperty($scope.selection,
|
||||
'length',
|
||||
{ get: function() {
|
||||
return this.selectedItems.length;
|
||||
}
|
||||
});
|
||||
|
||||
function rebuildSelections() {
|
||||
var _items = _($scope.items).chain();
|
||||
|
||||
$scope.selection.selectedItems =
|
||||
_items.filter(function(item) {
|
||||
return item.isSelected;
|
||||
}).pluck('value').value();
|
||||
|
||||
$scope.selection.deselectedItems =
|
||||
_items.pluck('value').difference($scope.selection.selectedItems)
|
||||
.value();
|
||||
|
||||
/**
|
||||
*
|
||||
* @ngdoc event
|
||||
* @name multiSelectList.selectionChanged
|
||||
* @eventOf multiSelectList.directive:multiSelectList
|
||||
*
|
||||
*/
|
||||
$scope.$emit('multiSelectList.selectionChanged', $scope.selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#registerItem
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Prepares an object to be tracked in the select list. Returns the
|
||||
* decorated item created by
|
||||
* {@link multiSelectList.controller:multiSelectList#decorateItem `decorateItem`}
|
||||
*/
|
||||
this.registerItem = function(item) {
|
||||
var decoratedItem = this.decorateItem(item);
|
||||
$scope.items = $scope.items.concat(decoratedItem);
|
||||
return decoratedItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#deregisterItem
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Removes an item from the list; called if the item is removed from the display
|
||||
* so that it is no longer tracked as a selectable item.
|
||||
*/
|
||||
this.deregisterItem = function(leavingItem) {
|
||||
$scope.items = $scope.items.filter(function(item) {
|
||||
return leavingItem !== item;
|
||||
});
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#decorateItem
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* This decorates an item with an object that has an `isSelected` property.
|
||||
* This value is used to determine the lists of selected and non-selected
|
||||
* items to emit with the `multiSelectList.selectionChanged`
|
||||
* event.
|
||||
*/
|
||||
this.decorateItem = function(item) {
|
||||
return {
|
||||
isSelected: false,
|
||||
value: item
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#selectAll
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Marks all items in the list as selected.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*/
|
||||
this.selectAll = function() {
|
||||
$scope.items.forEach(function(item) {
|
||||
item.isSelected = true;
|
||||
});
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#deselectAll
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Marks all items in the list as not selected.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*/
|
||||
this.deselectAll = function() {
|
||||
$scope.items.forEach(function(item) {
|
||||
item.isSelected = false;
|
||||
});
|
||||
$scope.selection.isExtended = false;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#deselectAllExtended
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Disables extended selection.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*/
|
||||
this.deselectAllExtended = function() {
|
||||
$scope.selection.isExtended = false;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#selectAllExtended
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Enables extended selection.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*/
|
||||
this.selectAllExtended = function() {
|
||||
$scope.selection.isExtended = true;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#selectItem
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Marks an item as selected.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*
|
||||
*/
|
||||
this.selectItem = function(item) {
|
||||
item.isSelected = true;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name multiSelectList.controller:multiSelectList#deregisterItem
|
||||
* @methodOf multiSelectList.controller:multiSelectList
|
||||
*
|
||||
* @description
|
||||
* Marks an item as not selected.
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*
|
||||
*/
|
||||
this.deselectItem = function(item) {
|
||||
item.isSelected = false;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
}];
|
||||
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name multiSelectList
|
||||
* @scope
|
||||
* @description Does some stuff
|
||||
*
|
||||
* @ngdoc directive
|
||||
* @name multiSelectList.directive:multiSelectList
|
||||
* @description
|
||||
* The `multiSelectList` directive works in conjunction with the
|
||||
* `selectListItem` and (optionally) the `selectAll` directives to
|
||||
* render checkboxes with list items and tracking the selected state
|
||||
* of each item. The `selectListItem` directive renders a checkbox,
|
||||
* and the `multiSelectList` directive tracks the selected state
|
||||
* of list items. The `selectAll` directive renders a checkbox that
|
||||
* will select/deselect all items in the list.
|
||||
*
|
||||
*
|
||||
* This directive exposes a special object on its local scope called
|
||||
* `selection` that is used to access the current selection state.
|
||||
* The following properties on `selection` are available:
|
||||
*
|
||||
* | Property | Type | Details |
|
||||
* |-------------------|-----------------|-------------------------------------------------------------|
|
||||
* | `selectedItems` | {@type array} | The items that are currently selected |
|
||||
* | `deselectedItem` | {@type array} | The items that are currently _not_ selected |
|
||||
* | `isExtended` | {@type boolean} | Indicates that the user has requested an extended selection |
|
||||
* | `length` | {@type number} | The length of the selected items array |
|
||||
*
|
||||
* Use the `multi-select-list` directive to indicate that you want
|
||||
* to allow users to select items in a list. To display a checkbox
|
||||
* next to each item, use the {@link multiSelectList.directive:selectListItem `select-list-item`} directive.
|
||||
*
|
||||
* # Rendering a basic multi-select list
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* This example creates a list of names and then
|
||||
* uses `multiSelectList` to make the names
|
||||
* selectable:
|
||||
*
|
||||
<example module="multiSelectList">
|
||||
<file name="index.html">
|
||||
<div ng-init="names =
|
||||
[ { name: 'blah'
|
||||
},
|
||||
{ name: 'diddy'
|
||||
},
|
||||
{ name: 'doo'
|
||||
},
|
||||
{ name: 'dah'
|
||||
},
|
||||
{ name: 'blah'
|
||||
}
|
||||
]">
|
||||
<ul multi-select-list>
|
||||
<li ng-repeat="item in names">
|
||||
<select-list-item item="item"></select-list-item>
|
||||
{{item.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
</example>
|
||||
*
|
||||
*/
|
||||
import controller from './multi-select-list.controller';
|
||||
|
||||
export default
|
||||
[ function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
},
|
||||
controller: controller
|
||||
};
|
||||
}];
|
||||
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name multiSelectList.directive:selectAll
|
||||
* @scope
|
||||
* @restrict E
|
||||
*
|
||||
* @param {string} label The text that will appear next to the checkbox
|
||||
* @param {number} itemsLength The number of displayed items in the list
|
||||
* @param {number} extendedItemsLength The total number of items in the list used for extended mode (see below)
|
||||
* @param {string_expression} extendedLabel A custom label to display when prompting the user to extend the selection; this is an expression so strings must be in single quotes ('), but you can use scope varibles here to display the count of items with the `extendedItemsLength` property
|
||||
* @param {boolean_expression} selectionsEmpty An expression that evaluates to a truthy value used to disable
|
||||
* the select all checkbox when the displayed list is empty
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use the `select-all` directive as a child of a `multi-select-list`
|
||||
* to present the user with a checkbox that, when checked, checks all
|
||||
* `select-list-item` children, and when unchecked it unchecks them.
|
||||
*
|
||||
* <example module="multiSelectList">
|
||||
<file name="index.html">
|
||||
<div ng-init="names =
|
||||
[ { name: 'blah'
|
||||
},
|
||||
{ name: 'diddy'
|
||||
},
|
||||
{ name: 'doo'
|
||||
},
|
||||
{ name: 'dah'
|
||||
},
|
||||
{ name: 'blah'
|
||||
}
|
||||
]">
|
||||
<ul multi-select-list>
|
||||
<li>
|
||||
<select-all label="Select All"></select-all>
|
||||
</li>
|
||||
<li ng-repeat="item in names">
|
||||
<select-list-item item="item"></select-list-item>
|
||||
{{item.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
* </example>
|
||||
*
|
||||
* ## Extended Selections
|
||||
*
|
||||
* In some cases the list items you are displaying are only a subset of
|
||||
* a larger list (eg. using pagination or infinite scroll to seperate
|
||||
* items). In these cases, when a user checks "select all", it may be
|
||||
* useful to give them the option to also select all the remaining
|
||||
* items in the list.
|
||||
*
|
||||
* This behavior is controlled by the `extendedItemsLength` property
|
||||
* of this directive. Set it to the total length of items in the list.
|
||||
* For example, if you have a list of 100 items, displayed 10 per page,
|
||||
* then `itemsLength` would be 10 and `extendedItemsLength` would be 100.
|
||||
* When the user checks "select all" in the above example, it will show
|
||||
* a button prompting them to "Select all 100 items". When the user selects
|
||||
* this option, the `select-all` directive tells the `multiSelectList`
|
||||
* controller that the selection is "extended" to all the items in the list.
|
||||
* Listeners to the `multiSelectList.selectionChanged` event can then use this
|
||||
* flag to respond differently when all items are selected.
|
||||
*
|
||||
*
|
||||
* <example module="extendedSelectionExample">
|
||||
<file name="app.js">
|
||||
angular.module('extendedSelectionExample', ['multiSelectList'])
|
||||
.controller('namesController', ['$scope', function($scope) {
|
||||
|
||||
var cleanup = $scope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
$scope.isSelectionExtended = selection.isExtended;
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', cleanup);
|
||||
|
||||
$scope.allNames =
|
||||
[ { name: 'John'
|
||||
},
|
||||
{ name: 'Jared'
|
||||
},
|
||||
{ name: 'Joe'
|
||||
},
|
||||
{ name: 'James'
|
||||
},
|
||||
{ name: 'Matt'
|
||||
},
|
||||
{ name: 'Luke'
|
||||
},
|
||||
{ name: 'Chris'
|
||||
}
|
||||
];
|
||||
|
||||
$scope.firstPageOfNames =
|
||||
$scope.allNames.slice(0,3);
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="namesController">
|
||||
<p ng-if="isSelectionExtended">Extended Selection</p>
|
||||
<ul multi-select-list>
|
||||
<li>
|
||||
<select-all
|
||||
label="Select All"
|
||||
selections-empty="selectedItems.length === 0"
|
||||
extended-items-length="allNames.length"
|
||||
items-length="firstPageOfNames.length"></select-all>
|
||||
</li>
|
||||
<li ng-repeat="item in firstPageOfNames">
|
||||
<select-list-item item="item"></select-list-item>
|
||||
{{item.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
*</example>
|
||||
*/
|
||||
// TODO: Extract to its own helper
|
||||
// Example:
|
||||
// template('shared/multi-select-list/select-all')
|
||||
// // =>
|
||||
// '/static/js/shared/multi-select-list/select-all.html
|
||||
//
|
||||
function template(base) {
|
||||
return '/static/js/' + base + '.partial.html';
|
||||
}
|
||||
|
||||
export default
|
||||
[ function() {
|
||||
return {
|
||||
require: '^multiSelectList',
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
label: '@',
|
||||
itemsLength: '=',
|
||||
extendedItemsLength: '=',
|
||||
extendedLabel: '&',
|
||||
isSelectionEmpty: '=selectionsEmpty'
|
||||
},
|
||||
templateUrl: template('shared/multi-select-list/select-all'),
|
||||
link: function(scope, element, attrs, controller) {
|
||||
|
||||
scope.label = scope.label || 'All';
|
||||
scope.selectExtendedLabel = scope.extendedLabel() || 'Select all ' + scope.extendedItemsLength + ' items';
|
||||
scope.deselectExtendedLabel = scope.deselectExtendedLabel || 'Deselect extra items';
|
||||
|
||||
scope.doSelectAll = function() {
|
||||
if (scope.isSelected) {
|
||||
controller.selectAll();
|
||||
|
||||
if (scope.supportsExtendedItems) {
|
||||
scope.showExtendedMessage = scope.itemsLength !== scope.extendedItemsLength;
|
||||
}
|
||||
} else {
|
||||
controller.deselectAll();
|
||||
|
||||
if (scope.isSelectionExtended) {
|
||||
scope.deselectAllExtended();
|
||||
}
|
||||
|
||||
scope.showExtendedMessage = false;
|
||||
}
|
||||
};
|
||||
|
||||
scope.$watch('extendedItemsLength', function(value) {
|
||||
scope.supportsExtendedItems = _.isNumber(value);
|
||||
});
|
||||
|
||||
scope.$watch('isSelectionEmpty', function(value) {
|
||||
if (value) {
|
||||
scope.isSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
scope.selectAllExtended = function() {
|
||||
controller.selectAllExtended(scope.extendedItemsLength);
|
||||
scope.isSelectionExtended = true;
|
||||
};
|
||||
|
||||
scope.deselectAllExtended = function() {
|
||||
controller.deselectAllExtended(scope.extendedItemsLength);
|
||||
scope.isSelectionExtended = false;
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -0,0 +1,18 @@
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
ng-disabled="itemsLength === 0"
|
||||
ng-model="isSelected"
|
||||
ng-change="doSelectAll()">
|
||||
{{label}}
|
||||
</label>
|
||||
<button
|
||||
ng-click="selectAllExtended()"
|
||||
ng-if="!isSelectionExtended && showExtendedMessage">
|
||||
{{selectExtendedLabel}}
|
||||
</button>
|
||||
<button
|
||||
ng-click="deselectAllExtended()"
|
||||
ng-if="isSelectionExtended && showExtendedMessage">
|
||||
{{deselectExtendedLabel}}
|
||||
</button>
|
||||
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name multiSelectList.directive:selectListItem
|
||||
* @restrict E
|
||||
* @scope
|
||||
* @description
|
||||
*
|
||||
The `select-list-item` directive renders a checkbox for tracking
|
||||
the state of a given item in a list. When the user checks the
|
||||
checkbox it tells the `multi-select-list` controller to select
|
||||
the item; when the user unchecks the checkbox it tells the controller
|
||||
to deselect the item.
|
||||
|
||||
@example
|
||||
|
||||
For examples of using this directive, see {@link multiSelectList.directive:multiSelectList multiSelectList}.
|
||||
|
||||
*/
|
||||
export default
|
||||
[ function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
item: '=item'
|
||||
},
|
||||
require: '^multiSelectList',
|
||||
template: '<input type="checkbox" ng-model="isSelected">',
|
||||
link: function(scope, element, attrs, multiSelectList) {
|
||||
|
||||
scope.isSelected = false;
|
||||
scope.decoratedItem = multiSelectList.registerItem(scope.item);
|
||||
|
||||
scope.$watch('isSelected', function(value) {
|
||||
if (value === true) {
|
||||
multiSelectList.selectItem(scope.decoratedItem);
|
||||
} else if (value === false) {
|
||||
multiSelectList.deselectItem(scope.decoratedItem);
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watch('decoratedItem.isSelected', function(value) {
|
||||
scope.isSelected = value;
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
multiSelectList.deregisterItem(scope.decoratedItem);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name widgets.function:DashboardJobs
|
||||
* @name widgets.function:PortalJobs
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
@ -14,3 +14,22 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.is-selected-row, .is-selected-row td {
|
||||
background-color: #E4F1FF !important;
|
||||
}
|
||||
|
||||
.select-column {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th.select-column {
|
||||
label {
|
||||
// overrides the default style of
|
||||
// display: inline-block, which allowed
|
||||
// margins to push down the label,
|
||||
// thus breaking the vertical-alignment
|
||||
// of the cell
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,8 @@ module.exports = function(config) {
|
||||
[ 'mocha',
|
||||
'chai',
|
||||
'sinon-chai',
|
||||
'chai-as-promised'
|
||||
'chai-as-promised',
|
||||
'chai-things'
|
||||
],
|
||||
|
||||
preprocessors:
|
||||
|
||||
@ -131,6 +131,7 @@ function TestDirective(name, deps) {
|
||||
afterCompile: function(fn) {
|
||||
|
||||
var self = this;
|
||||
var $outerScope;
|
||||
|
||||
// Make sure compile step gets setup first
|
||||
if (!this._compileRegistered) {
|
||||
@ -140,7 +141,7 @@ function TestDirective(name, deps) {
|
||||
// Then pre-apply the function with the outer scope
|
||||
self.withScope(function($scope) {
|
||||
// `this` refers to mocha test suite
|
||||
fn = fn.bind(this, $scope);
|
||||
$outerScope = $scope;
|
||||
});
|
||||
|
||||
// Finally, have it called by the isolate scope
|
||||
@ -150,7 +151,7 @@ function TestDirective(name, deps) {
|
||||
//
|
||||
self.withIsolateScope(function($scope) {
|
||||
// `this` refers to mocha test suite
|
||||
fn.apply(this, [$scope]);
|
||||
fn.apply(this, [$outerScope, $scope]);
|
||||
});
|
||||
|
||||
},
|
||||
@ -164,7 +165,11 @@ function TestDirective(name, deps) {
|
||||
}
|
||||
|
||||
beforeEach("compile directive element",
|
||||
inject(['$compile', '$httpBackend', function($compile, $httpBackend) {
|
||||
inject(['$compile', '$httpBackend', '$rootScope', function($compile, $httpBackend, $rootScope) {
|
||||
|
||||
if (!self.$scope) {
|
||||
self.$scope = $rootScope.$new();
|
||||
}
|
||||
|
||||
self.$element = $compile(self.element)(self.$scope);
|
||||
$(self.$element).appendTo('body');
|
||||
@ -200,6 +205,11 @@ function TestDirective(name, deps) {
|
||||
.whenGET(url)
|
||||
.respond(template);
|
||||
}]));
|
||||
},
|
||||
_ensureCompiled: function() {
|
||||
if (typeof this.$element === 'undefined') {
|
||||
throw "Can only call withController after registerPostHooks on directive test";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,197 @@
|
||||
import {describeModule} from 'tests/unit/describe-module';
|
||||
import mod from 'tower/shared/multi-select-list/main';
|
||||
|
||||
describeModule(mod.name)
|
||||
.testDirective('multiSelectList', function(test) {
|
||||
|
||||
var $scope;
|
||||
var controller;
|
||||
|
||||
test.use('<div multi-select-list></div>');
|
||||
|
||||
test.afterCompile(function(outerScope, scope) {
|
||||
$scope = scope;
|
||||
});
|
||||
|
||||
test.withController(function(_controller) {
|
||||
controller = _controller;
|
||||
});
|
||||
|
||||
it('works as an attribute on elements', function() {
|
||||
inject(['$compile', function($compile) {
|
||||
var node = $compile('<div multi-select-list></div>')($scope);
|
||||
var classes = Array.prototype.slice.apply(node[0].classList)
|
||||
expect(classes).to.contain('ng-scope');
|
||||
}]);
|
||||
});
|
||||
|
||||
context('controller init', function() {
|
||||
|
||||
it('initializes items and selection', function() {
|
||||
expect($scope.items).to.be.empty;
|
||||
expect($scope.selection.selectedItems).to.be.empty;
|
||||
expect($scope.selection.deselectedItems).to.be.empty;
|
||||
expect($scope.selection.isExtended).to.be.false;
|
||||
});
|
||||
|
||||
it('wraps items when they are registered', function() {
|
||||
var item = { name: 'blah' };
|
||||
var wrapped = controller.registerItem(item);
|
||||
|
||||
expect(wrapped.hasOwnProperty('isSelected')).to.be.true;
|
||||
expect(wrapped.hasOwnProperty('value')).to.be.true;
|
||||
|
||||
expect(wrapped.isSelected).to.be.false;
|
||||
expect(wrapped.value).to.eql(item);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
context('single select/deselect', function() {
|
||||
|
||||
it('marks item as selected/not selected', function() {
|
||||
var item = controller.registerItem({ name: 'blah' });
|
||||
controller.selectItem(item);
|
||||
|
||||
expect(item.isSelected).to.be.true;
|
||||
|
||||
controller.deselectItem(item);
|
||||
expect(item.isSelected).to.be.false;
|
||||
});
|
||||
|
||||
context('selectionChanged event', function() {
|
||||
|
||||
it('triggers on select/deselect', function() {
|
||||
var item = controller.registerItem({ name: 'blah' });
|
||||
var spy = sinon.spy();
|
||||
|
||||
$scope.$on('multiSelectList.selectionChanged', spy);
|
||||
|
||||
controller.selectItem(item);
|
||||
controller.deselectItem(item);
|
||||
|
||||
expect(spy).to.have.been.calledTwice;
|
||||
});
|
||||
|
||||
it('is called with the current selection', function() {
|
||||
var item = controller.registerItem({ name: 'blah' });
|
||||
var spy = sinon.spy();
|
||||
|
||||
$scope.$on('multiSelectList.selectionChanged', spy);
|
||||
|
||||
controller.selectItem(item);
|
||||
|
||||
expect(spy).to.have.been.calledWith(sinon.match.object,
|
||||
{ selectedItems:
|
||||
[ item.value
|
||||
],
|
||||
deselectedItems: [],
|
||||
isExtended: false
|
||||
});
|
||||
});
|
||||
|
||||
it('is called with deselections', function() {
|
||||
var item = controller.registerItem({ name: 'blah' });
|
||||
controller.selectItem(item);
|
||||
|
||||
var spy = sinon.spy();
|
||||
|
||||
|
||||
$scope.$on('multiSelectList.selectionChanged', spy);
|
||||
controller.deselectItem(item);
|
||||
|
||||
expect(spy).to.have.been.calledWith(sinon.match.object,
|
||||
{ selectedItems: [],
|
||||
deselectedItems:
|
||||
[ item.value
|
||||
],
|
||||
isExtended: false
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
context('select/deselect all items', function() {
|
||||
|
||||
it('marks all items as selected/deselected', function() {
|
||||
var item1 = controller.registerItem({ name: 'blah' });
|
||||
var item2 = controller.registerItem({ name: 'diddy' });
|
||||
var item3 = controller.registerItem({ name: 'doo' });
|
||||
|
||||
controller.selectAll();
|
||||
|
||||
expect([item1, item2, item3]).to.all.have.property('isSelected', true);
|
||||
|
||||
controller.deselectAll();
|
||||
|
||||
expect([item1, item2, item3]).to.all.have.property('isSelected', false);
|
||||
});
|
||||
|
||||
context('selectionChanged event', function() {
|
||||
|
||||
it('triggers with selections set to all the items', function() {
|
||||
var item1 = controller.registerItem({ name: 'blah' });
|
||||
var item2 = controller.registerItem({ name: 'diddy' });
|
||||
var item3 = controller.registerItem({ name: 'doo' });
|
||||
var spy = sinon.spy();
|
||||
|
||||
$scope.$on('multiSelectList.selectionChanged', spy);
|
||||
|
||||
controller.selectAll();
|
||||
|
||||
expect(spy).to.have.been.calledWith(
|
||||
sinon.match.object,
|
||||
{ selectedItems: _.pluck([item1, item2, item3], "value"),
|
||||
deselectedItems: [],
|
||||
isExtended: false
|
||||
});
|
||||
|
||||
controller.deselectAll();
|
||||
|
||||
expect(spy).to.have.been.calledWith(
|
||||
sinon.match.object,
|
||||
{ selectedItems: [],
|
||||
deselectedItems: _.pluck([item1, item2, item3], "value"),
|
||||
isExtended: false
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('tracks extended selection state', function() {
|
||||
var spy = sinon.spy();
|
||||
var item1 = controller.registerItem({ name: 'blah' });
|
||||
var item2 = controller.registerItem({ name: 'diddy' });
|
||||
var item3 = controller.registerItem({ name: 'doo' });
|
||||
var allItems = _.pluck([item1, item2, item3], 'value');
|
||||
|
||||
controller.selectAll();
|
||||
controller.selectAllExtended();
|
||||
|
||||
expect($scope.selection).to.have.property('isExtended', true);
|
||||
|
||||
controller.deselectAllExtended();
|
||||
|
||||
expect($scope.selection).to.have.property('isExtended', false);
|
||||
expect($scope.selection)
|
||||
.to.have.property('selectedItems')
|
||||
.that.is.an('array')
|
||||
.deep.equals(allItems);
|
||||
});
|
||||
|
||||
|
||||
it('toggles extended state on deselectAll', function() {
|
||||
controller.selectAllExtended();
|
||||
|
||||
controller.deselectAll();
|
||||
|
||||
expect($scope.selection).to.have.property('isExtended', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
import {describeModule} from 'tests/unit/describe-module';
|
||||
|
||||
var mockController = {
|
||||
selectAll: sinon.spy(),
|
||||
deselectAll: sinon.spy(),
|
||||
selectAllExtended: sinon.spy(),
|
||||
deselectAllExtended: sinon.spy()
|
||||
};
|
||||
|
||||
describeModule('multiSelectList')
|
||||
.testDirective('selectAll', function(directive) {
|
||||
|
||||
var $scope;
|
||||
|
||||
directive.use('<fake-parent><select-all selections-empty="isEmpty" extended-items-length="numItems"></select-all></fake-parent>');
|
||||
|
||||
beforeEach(function() {
|
||||
directive.element.data('$multiSelectListController', mockController);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
mockController.selectAll.reset();
|
||||
mockController.deselectAll.reset();
|
||||
mockController.selectAllExtended.reset();
|
||||
mockController.deselectAllExtended.reset();
|
||||
});
|
||||
|
||||
directive.afterCompile(function() {
|
||||
|
||||
// Since we had to wrap select-all in a fake directive
|
||||
// to mock the controller, we have to reach down to
|
||||
// get it's isolate scope
|
||||
//
|
||||
$scope =
|
||||
directive.$element.find('select-all').isolateScope();
|
||||
});
|
||||
|
||||
it('works as an element tag', function() {
|
||||
var classes = Array.prototype.slice.apply(directive.$element[0].classList);
|
||||
expect(classes).to.contain('ng-scope');
|
||||
});
|
||||
|
||||
it('calls select all when isSelected is true', function() {
|
||||
$scope.isSelected = true;
|
||||
$scope.doSelectAll();
|
||||
expect(mockController.selectAll).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('calls deselect all when isSelected is false', function() {
|
||||
$scope.isSelected = false;
|
||||
$scope.doSelectAll();
|
||||
|
||||
expect(mockController.deselectAll).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('calls deselect all extended when deselecting all', function() {
|
||||
$scope.isSelected = false;
|
||||
$scope.isSelectionExtended = true;
|
||||
$scope.doSelectAll();
|
||||
|
||||
expect(mockController.deselectAllExtended).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
context('input parameters', function() {
|
||||
|
||||
var $outerScope;
|
||||
|
||||
// We need to grab the parent scope object so we can control
|
||||
// the parameters that are passed into the directive in the
|
||||
// `use` call above
|
||||
directive.withScope(function(_outerScope) {
|
||||
$outerScope = _outerScope;
|
||||
});
|
||||
|
||||
it('when true sets isSelected to false', function() {
|
||||
|
||||
$scope.isSelected = true;
|
||||
$outerScope.isEmpty = true;
|
||||
$outerScope.$apply();
|
||||
|
||||
expect($scope).to.have.property('isSelected', false);
|
||||
});
|
||||
|
||||
it('sets supportsExtendedItems when extendedItemsLength is given', function() {
|
||||
$scope.supportsExtendedItems = false;
|
||||
$outerScope.numItems = 5;
|
||||
$outerScope.$apply();
|
||||
|
||||
expect($scope).to.have.property('supportsExtendedItems', true);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user