on branch deletion show loading icon and disabled the button
after user click delete branch, there is no processing indication, and user can click many times till. It seems flaw in UX. this will fix it fix bug in branch deletion link
This commit is contained in:
parent
510c51a6b5
commit
94255217e9
|
@ -0,0 +1,35 @@
|
|||
class AjaxLoadingSpinner {
|
||||
static init() {
|
||||
const $elements = $('.js-ajax-loading-spinner');
|
||||
|
||||
$elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
$elements.on('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
|
||||
}
|
||||
|
||||
static ajaxBeforeSend(e) {
|
||||
e.target.setAttribute('disabled', '');
|
||||
const iconElement = e.target.querySelector('i');
|
||||
// get first fa- icon
|
||||
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
|
||||
iconElement.dataset.icon = originalIcon;
|
||||
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
|
||||
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
}
|
||||
|
||||
static ajaxComplete(e) {
|
||||
e.target.removeAttribute('disabled');
|
||||
const iconElement = e.target.querySelector('i');
|
||||
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
|
||||
$(e.target).off('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
|
||||
}
|
||||
|
||||
static toggleLoadingIcon(iconElement) {
|
||||
const classList = iconElement.classList;
|
||||
classList.toggle(iconElement.dataset.icon);
|
||||
classList.toggle('fa-spinner');
|
||||
classList.toggle('fa-spin');
|
||||
}
|
||||
}
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.AjaxLoadingSpinner = AjaxLoadingSpinner;
|
|
@ -108,6 +108,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
|
|||
case 'projects:compare:show':
|
||||
new gl.Diff();
|
||||
break;
|
||||
case 'projects:branches:index':
|
||||
gl.AjaxLoadingSpinner.init();
|
||||
break;
|
||||
case 'projects:issues:new':
|
||||
case 'projects:issues:edit':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
- if can?(current_user, :push_code, @project)
|
||||
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name),
|
||||
class: "btn btn-remove remove-row #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}",
|
||||
class: "btn btn-remove remove-row js-ajax-loading-spinner #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}",
|
||||
method: :delete,
|
||||
data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" },
|
||||
remote: true,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: on branch deletion show loading icon and disabled the button
|
||||
merge_request: 6761
|
||||
author: wendy0402
|
|
@ -0,0 +1,58 @@
|
|||
require('~/extensions/array');
|
||||
require('jquery');
|
||||
require('jquery-ujs');
|
||||
require('~/ajax_loading_spinner');
|
||||
|
||||
describe('Ajax Loading Spinner', () => {
|
||||
const fixtureTemplate = 'static/ajax_loading_spinner.html.raw';
|
||||
preloadFixtures(fixtureTemplate);
|
||||
|
||||
beforeEach(() => {
|
||||
loadFixtures(fixtureTemplate);
|
||||
gl.AjaxLoadingSpinner.init();
|
||||
});
|
||||
|
||||
it('change current icon with spinner icon and disable link while waiting ajax response', (done) => {
|
||||
spyOn(jQuery, 'ajax').and.callFake((req) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
|
||||
const icon = ajaxLoadingSpinner.querySelector('i');
|
||||
|
||||
req.beforeSend(xhr, { dataType: 'text/html' });
|
||||
|
||||
expect(icon).not.toHaveClass('fa-trash-o');
|
||||
expect(icon).toHaveClass('fa-spinner');
|
||||
expect(icon).toHaveClass('fa-spin');
|
||||
expect(icon.dataset.icon).toEqual('fa-trash-o');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual('');
|
||||
|
||||
req.complete({});
|
||||
|
||||
done();
|
||||
const deferred = $.Deferred();
|
||||
return deferred.promise();
|
||||
});
|
||||
document.querySelector('.js-ajax-loading-spinner').click();
|
||||
});
|
||||
|
||||
it('use original icon again and enabled the link after complete the ajax request', (done) => {
|
||||
spyOn(jQuery, 'ajax').and.callFake((req) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
|
||||
|
||||
req.beforeSend(xhr, { dataType: 'text/html' });
|
||||
req.complete({});
|
||||
|
||||
const icon = ajaxLoadingSpinner.querySelector('i');
|
||||
expect(icon).toHaveClass('fa-trash-o');
|
||||
expect(icon).not.toHaveClass('fa-spinner');
|
||||
expect(icon).not.toHaveClass('fa-spin');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null);
|
||||
|
||||
done();
|
||||
const deferred = $.Deferred();
|
||||
return deferred.promise();
|
||||
});
|
||||
document.querySelector('.js-ajax-loading-spinner').click();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
%a.js-ajax-loading-spinner{href: "http://goesnowhere.nothing/whereami", data: {remote: true}}
|
||||
%i.fa.fa-trash-o
|
Loading…
Reference in New Issue