Merge "Replaces the delete button with a disabling version"
This commit is contained in:
commit
235d4c5e9e
@ -38,7 +38,8 @@
|
||||
'horizon.app.core.images.actions.delete-image.service',
|
||||
'horizon.app.core.images.actions.launch-instance.service',
|
||||
'horizon.app.core.images.actions.update-metadata.service',
|
||||
'horizon.app.core.images.resourceType'
|
||||
'horizon.app.core.images.resourceType',
|
||||
'horizon.app.core.images.basePath'
|
||||
];
|
||||
|
||||
function registerImageActions(
|
||||
@ -49,7 +50,8 @@
|
||||
deleteImageService,
|
||||
launchInstanceService,
|
||||
updateMetadataService,
|
||||
imageResourceTypeCode
|
||||
imageResourceTypeCode,
|
||||
basePath
|
||||
) {
|
||||
var imageResourceType = registry.getResourceType(imageResourceTypeCode);
|
||||
imageResourceType.itemActions
|
||||
@ -100,15 +102,18 @@
|
||||
}
|
||||
});
|
||||
|
||||
// A custom template is provided instead of the 'standard' definition
|
||||
// to customize when the rendered button is disabled
|
||||
//
|
||||
// The template contains a new angular component which controls the
|
||||
// disabled/enabled state of the rendered button.
|
||||
imageResourceType.batchActions
|
||||
.append({
|
||||
id: 'batchDeleteImageAction',
|
||||
service: deleteImageService,
|
||||
template: {
|
||||
type: 'delete-selected',
|
||||
text: gettext('Delete Images')
|
||||
url: basePath + "/actions/delete-image-selected-button.template.html"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -0,0 +1 @@
|
||||
<delete-image-selected selected="tCtrl.selected"></delete-image-selected>
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use self file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
// The component generally renders the same content as the default batch action
|
||||
// button with the added complexity of changing the buttons enabled/disabled
|
||||
// stated based on the 'allowed' state of the passed selected images.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.images.actions')
|
||||
.component('deleteImageSelected', {
|
||||
controller: controller,
|
||||
templateUrl: templateUrl,
|
||||
bindings: {
|
||||
callback: '=?',
|
||||
selected: '<'
|
||||
}
|
||||
});
|
||||
|
||||
controller.$inject = [
|
||||
'horizon.app.core.images.actions.delete-image.service',
|
||||
'$q'
|
||||
];
|
||||
|
||||
function controller(deleteImageService, $q) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.$onInit = function() {
|
||||
ctrl.text = gettext('Delete Images');
|
||||
ctrl._disable();
|
||||
};
|
||||
|
||||
ctrl.$onChanges = function() {
|
||||
ctrl._disable();
|
||||
};
|
||||
|
||||
ctrl._disable = function() {
|
||||
if (ctrl.selected.length === 0) {
|
||||
ctrl.disabled = true;
|
||||
} else {
|
||||
var promises = $.map(ctrl.selected, function(image) {
|
||||
return deleteImageService.allowed(image);
|
||||
});
|
||||
|
||||
$q.all(promises).then(
|
||||
function() {
|
||||
ctrl.disabled = false;
|
||||
},
|
||||
function() {
|
||||
ctrl.disabled = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
templateUrl.$inject = ['horizon.app.core.images.basePath'];
|
||||
|
||||
function templateUrl(basePath) {
|
||||
return basePath + 'actions/delete-image-selected.template.html';
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use self file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
describe('delete-image-selected component', function() {
|
||||
var $scope, $element, $controller, $q;
|
||||
// Mock image data
|
||||
var mockAllowed = { allowed: true };
|
||||
var mockDisallowed = { allowed: false };
|
||||
|
||||
beforeEach(module('templates'));
|
||||
beforeEach(module('horizon.app.core.images.actions', function($provide) {
|
||||
// Injects a mock 'action' directive for unit testing
|
||||
$provide.decorator('actionDirective', function($delegate) {
|
||||
var component = $delegate[0];
|
||||
|
||||
component.template = '<div>Mock</div>';
|
||||
component.templateUrl = null;
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
|
||||
// Mock delete-image.service. The disabling mechanism uses the allowed
|
||||
// function from that service using the promises API, which is mocked
|
||||
// here.
|
||||
$provide.service(
|
||||
'horizon.app.core.images.actions.delete-image.service', function() {
|
||||
return {
|
||||
allowed: function(mockImage) {
|
||||
var deferred = $q.defer();
|
||||
if (mockImage.allowed) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject();
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
}));
|
||||
beforeEach(inject(function(_$rootScope_, _$compile_, _$q_) {
|
||||
$q = _$q_;
|
||||
$scope = _$rootScope_.$new();
|
||||
var tag = angular.element(
|
||||
'<delete-image-selected selected="selected" callback="callback">' +
|
||||
'</delete-image-selected>'
|
||||
);
|
||||
|
||||
$scope.selected = [];
|
||||
|
||||
$element = _$compile_(tag)($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$controller = $element.controller('deleteImageSelected');
|
||||
}));
|
||||
|
||||
it('disables for empty list', function() {
|
||||
expect($controller.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('enables for all allowed images', function() {
|
||||
// Selections change the object; just pushing in new values wouldn't
|
||||
// trigger disable recalculations
|
||||
$scope.selected = [$.extend({}, mockAllowed)];
|
||||
$scope.$apply();
|
||||
expect($controller.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('disables for all disallowed images', function() {
|
||||
$scope.selected = [$.extend({}, mockDisallowed)];
|
||||
$scope.$apply();
|
||||
expect($controller.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('disables for mixed images', function() {
|
||||
$scope.selected = [
|
||||
$.extend({}, mockDisallowed),
|
||||
$.extend({}, mockDisallowed)
|
||||
];
|
||||
$scope.$apply();
|
||||
expect($controller.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
})();
|
@ -0,0 +1,7 @@
|
||||
<action action-classes="'btn btn-danger'"
|
||||
disabled="$ctrl.disabled"
|
||||
item="$ctrl.selected"
|
||||
callback="$ctrl.callback">
|
||||
<span class="fa fa-trash"></span>
|
||||
{{ $ctrl.text }}
|
||||
</action>
|
Loading…
Reference in New Issue
Block a user