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:
wendy0402 2016-10-09 21:17:14 +07:00
parent 510c51a6b5
commit 94255217e9
6 changed files with 103 additions and 1 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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,

View File

@ -0,0 +1,4 @@
---
title: on branch deletion show loading icon and disabled the button
merge_request: 6761
author: wendy0402

View File

@ -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();
});
});

View File

@ -0,0 +1,2 @@
%a.js-ajax-loading-spinner{href: "http://goesnowhere.nothing/whereami", data: {remote: true}}
%i.fa.fa-trash-o